First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
<?php
interface ICallbackNamed {
function hasName();
function getName();
}
/**
* Callback class introduces currying-like pattern.
*
* Example:
* function foo($param1, $param2, $param3) {
* var_dump($param1, $param2, $param3);
* }
* $fooCurried = new Callback('foo',
* 'param1 is now statically set',
* new CallbackParam, new CallbackParam
* );
* phpQuery::callbackRun($fooCurried,
* array('param2 value', 'param3 value'
* );
*
* Callback class is supported in all phpQuery methods which accepts callbacks.
*
* @link http://code.google.com/p/phpquery/wiki/Callbacks#Param_Structures
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*
* @TODO??? return fake forwarding function created via create_function
* @TODO honor paramStructure
*/
class Callback
implements ICallbackNamed {
public $callback = null;
public $params = null;
protected $name;
public function __construct($callback, $param1 = null, $param2 = null,
$param3 = null) {
$params = func_get_args();
$params = array_slice($params, 1);
if ($callback instanceof Callback) {
// TODO implement recurention
} else {
$this->callback = $callback;
$this->params = $params;
}
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
// TODO test me
// public function addParams() {
// $params = func_get_args();
// return new Callback($this->callback, $this->params+$params);
// }
}
/**
* Shorthand for new Callback(create_function(...), ...);
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackBody extends Callback {
public function __construct($paramList, $code, $param1 = null, $param2 = null,
$param3 = null) {
$params = func_get_args();
$params = array_slice($params, 2);
$this->callback = create_function($paramList, $code);
$this->params = $params;
}
}
/**
* Callback type which on execution returns reference passed during creation.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackReturnReference extends Callback
implements ICallbackNamed {
protected $reference;
public function __construct(&$reference, $name = null){
$this->reference =& $reference;
$this->callback = array($this, 'callback');
}
public function callback() {
return $this->reference;
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
}
/**
* Callback type which on execution returns value passed during creation.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackReturnValue extends Callback
implements ICallbackNamed {
protected $value;
protected $name;
public function __construct($value, $name = null){
$this->value =& $value;
$this->name = $name;
$this->callback = array($this, 'callback');
}
public function callback() {
return $this->value;
}
public function __toString() {
return $this->getName();
}
public function getName() {
return 'Callback: '.$this->name;
}
public function hasName() {
return isset($this->name) && $this->name;
}
}
/**
* CallbackParameterToReference can be used when we don't really want a callback,
* only parameter passed to it. CallbackParameterToReference takes first
* parameter's value and passes it to reference.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
class CallbackParameterToReference extends Callback {
/**
* @param $reference
* @TODO implement $paramIndex;
* param index choose which callback param will be passed to reference
*/
public function __construct(&$reference){
$this->callback =& $reference;
}
}
//class CallbackReference extends Callback {
// /**
// *
// * @param $reference
// * @param $paramIndex
// * @todo implement $paramIndex; param index choose which callback param will be passed to reference
// */
// public function __construct(&$reference, $name = null){
// $this->callback =& $reference;
// }
//}
class CallbackParam {}

View file

@ -0,0 +1,681 @@
<?php
/**
* DOMDocumentWrapper class simplifies work with DOMDocument.
*
* Know bug:
* - in XHTML fragments, <br /> changes to <br clear="none" />
*
* @todo check XML catalogs compatibility
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
class DOMDocumentWrapper {
/**
* @var DOMDocument
*/
public $document;
public $id;
/**
* @todo Rewrite as method and quess if null.
* @var unknown_type
*/
public $contentType = '';
public $xpath;
public $uuid = 0;
public $data = array();
public $dataNodes = array();
public $events = array();
public $eventsNodes = array();
public $eventsGlobal = array();
/**
* @TODO iframes support http://code.google.com/p/phpquery/issues/detail?id=28
* @var unknown_type
*/
public $frames = array();
/**
* Document root, by default equals to document itself.
* Used by documentFragments.
*
* @var DOMNode
*/
public $root;
public $isDocumentFragment;
public $isXML = false;
public $isXHTML = false;
public $isHTML = false;
public $charset;
public function __construct($markup = null, $contentType = null, $newDocumentID = null) {
if (isset($markup))
$this->load($markup, $contentType, $newDocumentID);
$this->id = $newDocumentID
? $newDocumentID
: md5(microtime());
}
public function load($markup, $contentType = null, $newDocumentID = null) {
// phpQuery::$documents[$id] = $this;
$this->contentType = strtolower($contentType);
if ($markup instanceof DOMDOCUMENT) {
$this->document = $markup;
$this->root = $this->document;
$this->charset = $this->document->encoding;
// TODO isDocumentFragment
$loaded = true;
} else {
$loaded = $this->loadMarkup($markup);
}
if ($loaded) {
// $this->document->formatOutput = true;
$this->document->preserveWhiteSpace = true;
$this->xpath = new DOMXPath($this->document);
$this->afterMarkupLoad();
return true;
// remember last loaded document
// return phpQuery::selectDocument($id);
}
return false;
}
protected function afterMarkupLoad() {
if ($this->isXHTML) {
$this->xpath->registerNamespace("html", "http://www.w3.org/1999/xhtml");
}
}
protected function loadMarkup($markup) {
$loaded = false;
if ($this->contentType) {
self::debug("Load markup for content type {$this->contentType}");
// content determined by contentType
list($contentType, $charset) = $this->contentTypeToArray($this->contentType);
switch($contentType) {
case 'text/html':
phpQuery::debug("Loading HTML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupHTML($markup, $charset);
break;
case 'text/xml':
case 'application/xhtml+xml':
phpQuery::debug("Loading XML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupXML($markup, $charset);
break;
default:
// for feeds or anything that sometimes doesn't use text/xml
if (strpos('xml', $this->contentType) !== false) {
phpQuery::debug("Loading XML, content type '{$this->contentType}'");
$loaded = $this->loadMarkupXML($markup, $charset);
} else
phpQuery::debug("Could not determine document type from content type '{$this->contentType}'");
}
} else {
// content type autodetection
if ($this->isXML($markup)) {
phpQuery::debug("Loading XML, isXML() == true");
$loaded = $this->loadMarkupXML($markup);
if (! $loaded && $this->isXHTML) {
phpQuery::debug('Loading as XML failed, trying to load as HTML, isXHTML == true');
$loaded = $this->loadMarkupHTML($markup);
}
} else {
phpQuery::debug("Loading HTML, isXML() == false");
$loaded = $this->loadMarkupHTML($markup);
}
}
return $loaded;
}
protected function loadMarkupReset() {
$this->isXML = $this->isXHTML = $this->isHTML = false;
}
protected function documentCreate($charset, $version = '1.0') {
if (! $version)
$version = '1.0';
$this->document = new DOMDocument($version, $charset);
$this->charset = $this->document->encoding;
// $this->document->encoding = $charset;
$this->document->formatOutput = true;
$this->document->preserveWhiteSpace = true;
}
protected function loadMarkupHTML($markup, $requestedCharset = null) {
if (phpQuery::$debug)
phpQuery::debug('Full markup load (HTML): '.substr($markup, 0, 250));
$this->loadMarkupReset();
$this->isHTML = true;
if (!isset($this->isDocumentFragment))
$this->isDocumentFragment = self::isDocumentFragmentHTML($markup);
$charset = null;
$documentCharset = $this->charsetFromHTML($markup);
$addDocumentCharset = false;
if ($documentCharset) {
$charset = $documentCharset;
$markup = $this->charsetFixHTML($markup);
} else if ($requestedCharset) {
$charset = $requestedCharset;
}
if (! $charset)
$charset = phpQuery::$defaultCharset;
// HTTP 1.1 says that the default charset is ISO-8859-1
// @see http://www.w3.org/International/O-HTTP-charset
if (! $documentCharset) {
$documentCharset = 'ISO-8859-1';
$addDocumentCharset = true;
}
// Should be careful here, still need 'magic encoding detection' since lots of pages have other 'default encoding'
// Worse, some pages can have mixed encodings... we'll try not to worry about that
$requestedCharset = strtoupper($requestedCharset);
$documentCharset = strtoupper($documentCharset);
phpQuery::debug("DOC: $documentCharset REQ: $requestedCharset");
if ($requestedCharset && $documentCharset && $requestedCharset !== $documentCharset) {
phpQuery::debug("CHARSET CONVERT");
// Document Encoding Conversion
// http://code.google.com/p/phpquery/issues/detail?id=86
if (function_exists('mb_detect_encoding')) {
$possibleCharsets = array($documentCharset, $requestedCharset, 'AUTO');
$docEncoding = mb_detect_encoding($markup, implode(', ', $possibleCharsets));
if (! $docEncoding)
$docEncoding = $documentCharset; // ok trust the document
phpQuery::debug("DETECTED '$docEncoding'");
// Detected does not match what document says...
if ($docEncoding !== $documentCharset) {
// Tricky..
}
if ($docEncoding !== $requestedCharset) {
phpQuery::debug("CONVERT $docEncoding => $requestedCharset");
$markup = mb_convert_encoding($markup, $requestedCharset, $docEncoding);
$markup = $this->charsetAppendToHTML($markup, $requestedCharset);
$charset = $requestedCharset;
}
} else {
phpQuery::debug("TODO: charset conversion without mbstring...");
}
}
$return = false;
if ($this->isDocumentFragment) {
phpQuery::debug("Full markup load (HTML), DocumentFragment detected, using charset '$charset'");
$return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
} else {
if ($addDocumentCharset) {
phpQuery::debug("Full markup load (HTML), appending charset: '$charset'");
$markup = $this->charsetAppendToHTML($markup, $charset);
}
phpQuery::debug("Full markup load (HTML), documentCreate('$charset')");
$this->documentCreate($charset);
$return = phpQuery::$debug === 2
? $this->document->loadHTML($markup)
: @$this->document->loadHTML($markup);
if ($return)
$this->root = $this->document;
}
if ($return && ! $this->contentType)
$this->contentType = 'text/html';
return $return;
}
protected function loadMarkupXML($markup, $requestedCharset = null) {
if (phpQuery::$debug)
phpQuery::debug('Full markup load (XML): '.substr($markup, 0, 250));
$this->loadMarkupReset();
$this->isXML = true;
// check agains XHTML in contentType or markup
$isContentTypeXHTML = $this->isXHTML();
$isMarkupXHTML = $this->isXHTML($markup);
if ($isContentTypeXHTML || $isMarkupXHTML) {
self::debug('Full markup load (XML), XHTML detected');
$this->isXHTML = true;
}
// determine document fragment
if (! isset($this->isDocumentFragment))
$this->isDocumentFragment = $this->isXHTML
? self::isDocumentFragmentXHTML($markup)
: self::isDocumentFragmentXML($markup);
// this charset will be used
$charset = null;
// charset from XML declaration @var string
$documentCharset = $this->charsetFromXML($markup);
if (! $documentCharset) {
if ($this->isXHTML) {
// this is XHTML, try to get charset from content-type meta header
$documentCharset = $this->charsetFromHTML($markup);
if ($documentCharset) {
phpQuery::debug("Full markup load (XML), appending XHTML charset '$documentCharset'");
$this->charsetAppendToXML($markup, $documentCharset);
$charset = $documentCharset;
}
}
if (! $documentCharset) {
// if still no document charset...
$charset = $requestedCharset;
}
} else if ($requestedCharset) {
$charset = $requestedCharset;
}
if (! $charset) {
$charset = phpQuery::$defaultCharset;
}
if ($requestedCharset && $documentCharset && $requestedCharset != $documentCharset) {
// TODO place for charset conversion
// $charset = $requestedCharset;
}
$return = false;
if ($this->isDocumentFragment) {
phpQuery::debug("Full markup load (XML), DocumentFragment detected, using charset '$charset'");
$return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
} else {
// FIXME ???
if ($isContentTypeXHTML && ! $isMarkupXHTML)
if (! $documentCharset) {
phpQuery::debug("Full markup load (XML), appending charset '$charset'");
$markup = $this->charsetAppendToXML($markup, $charset);
}
// see http://pl2.php.net/manual/en/book.dom.php#78929
// LIBXML_DTDLOAD (>= PHP 5.1)
// does XML ctalogues works with LIBXML_NONET
// $this->document->resolveExternals = true;
// TODO test LIBXML_COMPACT for performance improvement
// create document
$this->documentCreate($charset);
if (phpversion() < 5.1) {
$this->document->resolveExternals = true;
$return = phpQuery::$debug === 2
? $this->document->loadXML($markup)
: @$this->document->loadXML($markup);
} else {
/** @link http://pl2.php.net/manual/en/libxml.constants.php */
$libxmlStatic = phpQuery::$debug === 2
? LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET
: LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOERROR;
$return = $this->document->loadXML($markup, $libxmlStatic);
// if (! $return)
// $return = $this->document->loadHTML($markup);
}
if ($return)
$this->root = $this->document;
}
if ($return) {
if (! $this->contentType) {
if ($this->isXHTML)
$this->contentType = 'application/xhtml+xml';
else
$this->contentType = 'text/xml';
}
return $return;
} else {
throw new Exception("Error loading XML markup");
}
}
protected function isXHTML($markup = null) {
if (! isset($markup)) {
return strpos($this->contentType, 'xhtml') !== false;
}
// XXX ok ?
return strpos($markup, "<!DOCTYPE html") !== false;
// return stripos($doctype, 'xhtml') !== false;
// $doctype = isset($dom->doctype) && is_object($dom->doctype)
// ? $dom->doctype->publicId
// : self::$defaultDoctype;
}
protected function isXML($markup) {
// return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
}
protected function contentTypeToArray($contentType) {
$test = null;
$test =
$matches = explode(';', trim(strtolower($contentType)));
if (isset($matches[1])) {
$matches[1] = explode('=', $matches[1]);
// strip 'charset='
$matches[1] = isset($matches[1][1]) && trim($matches[1][1])
? $matches[1][1]
: $matches[1][0];
} else
$matches[1] = null;
return $matches;
}
/**
*
* @param $markup
* @return array contentType, charset
*/
protected function contentTypeFromHTML($markup) {
$matches = array();
// find meta tag
preg_match('@<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
$markup, $matches
);
if (! isset($matches[0]))
return array(null, null);
// get attr 'content'
preg_match('@content\\s*=\\s*(["|\'])(.+?)\\1@', $matches[0], $matches);
if (! isset($matches[0]))
return array(null, null);
return $this->contentTypeToArray($matches[2]);
}
protected function charsetFromHTML($markup) {
$contentType = $this->contentTypeFromHTML($markup);
return $contentType[1];
}
protected function charsetFromXML($markup) {
$matches;
// find declaration
preg_match('@<'.'?xml[^>]+encoding\\s*=\\s*(["|\'])(.*?)\\1@i',
$markup, $matches
);
return isset($matches[2])
? strtolower($matches[2])
: null;
}
/**
* Repositions meta[type=charset] at the start of head. Bypasses DOMDocument bug.
*
* @link http://code.google.com/p/phpquery/issues/detail?id=80
* @param $html
*/
protected function charsetFixHTML($markup) {
$matches = array();
// find meta tag
preg_match('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
$markup, $matches, PREG_OFFSET_CAPTURE
);
if (! isset($matches[0]))
return;
$metaContentType = $matches[0][0];
$markup = substr($markup, 0, $matches[0][1])
.substr($markup, $matches[0][1]+strlen($metaContentType));
$headStart = stripos($markup, '<head>');
$markup = substr($markup, 0, $headStart+6).$metaContentType
.substr($markup, $headStart+6);
return $markup;
}
protected function charsetAppendToHTML($html, $charset, $xhtml = false) {
// remove existing meta[type=content-type]
$html = preg_replace('@\s*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
$meta = '<meta http-equiv="Content-Type" content="text/html;charset='
.$charset.'" '
.($xhtml ? '/' : '')
.'>';
if (strpos($html, '<head') === false) {
if (strpos($html, '<html') === false) {
return $meta.$html;
} else {
return preg_replace(
'@<html(.*?)(?(?<!\?)>)@s',
"<html\\1><head>{$meta}</head>",
$html
);
}
} else {
return preg_replace(
'@<head(.*?)(?(?<!\?)>)@s',
'<head\\1>'.$meta,
$html
);
}
}
protected function charsetAppendToXML($markup, $charset) {
$declaration = '<'.'?xml version="1.0" encoding="'.$charset.'"?'.'>';
return $declaration.$markup;
}
public static function isDocumentFragmentHTML($markup) {
return stripos($markup, '<html') === false && stripos($markup, '<!doctype') === false;
}
public static function isDocumentFragmentXML($markup) {
return stripos($markup, '<'.'?xml') === false;
}
public static function isDocumentFragmentXHTML($markup) {
return self::isDocumentFragmentHTML($markup);
}
public function importAttr($value) {
// TODO
}
/**
*
* @param $source
* @param $target
* @param $sourceCharset
* @return array Array of imported nodes.
*/
public function import($source, $sourceCharset = null) {
// TODO charset conversions
$return = array();
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
$source = array($source);
// if (is_array($source)) {
// foreach($source as $node) {
// if (is_string($node)) {
// // string markup
// $fake = $this->documentFragmentCreate($node, $sourceCharset);
// if ($fake === false)
// throw new Exception("Error loading documentFragment markup");
// else
// $return = array_merge($return,
// $this->import($fake->root->childNodes)
// );
// } else {
// $return[] = $this->document->importNode($node, true);
// }
// }
// return $return;
// } else {
// // string markup
// $fake = $this->documentFragmentCreate($source, $sourceCharset);
// if ($fake === false)
// throw new Exception("Error loading documentFragment markup");
// else
// return $this->import($fake->root->childNodes);
// }
if (is_array($source) || $source instanceof DOMNODELIST) {
// dom nodes
self::debug('Importing nodes to document');
foreach($source as $node)
$return[] = $this->document->importNode($node, true);
} else {
// string markup
$fake = $this->documentFragmentCreate($source, $sourceCharset);
if ($fake === false)
throw new Exception("Error loading documentFragment markup");
else
return $this->import($fake->root->childNodes);
}
return $return;
}
/**
* Creates new document fragment.
*
* @param $source
* @return DOMDocumentWrapper
*/
protected function documentFragmentCreate($source, $charset = null) {
$fake = new DOMDocumentWrapper();
$fake->contentType = $this->contentType;
$fake->isXML = $this->isXML;
$fake->isHTML = $this->isHTML;
$fake->isXHTML = $this->isXHTML;
$fake->root = $fake->document;
if (! $charset)
$charset = $this->charset;
// $fake->documentCreate($this->charset);
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
$source = array($source);
if (is_array($source) || $source instanceof DOMNODELIST) {
// dom nodes
// load fake document
if (! $this->documentFragmentLoadMarkup($fake, $charset))
return false;
$nodes = $fake->import($source);
foreach($nodes as $node)
$fake->root->appendChild($node);
} else {
// string markup
$this->documentFragmentLoadMarkup($fake, $charset, $source);
}
return $fake;
}
/**
*
* @param $document DOMDocumentWrapper
* @param $markup
* @return $document
*/
private function documentFragmentLoadMarkup($fragment, $charset, $markup = null) {
// TODO error handling
// TODO copy doctype
// tempolary turn off
$fragment->isDocumentFragment = false;
if ($fragment->isXML) {
if ($fragment->isXHTML) {
// add FAKE element to set default namespace
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?>'
.'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
.'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
.'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
$fragment->root = $fragment->document->firstChild->nextSibling;
} else {
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
$fragment->root = $fragment->document->firstChild;
}
} else {
$markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
.$charset.'"></head>';
$noBody = strpos($markup, '<body') === false;
if ($noBody)
$markup2 .= '<body>';
$markup2 .= $markup;
if ($noBody)
$markup2 .= '</body>';
$markup2 .= '</html>';
$fragment->loadMarkupHTML($markup2);
// TODO resolv body tag merging issue
$fragment->root = $noBody
? $fragment->document->firstChild->nextSibling->firstChild->nextSibling
: $fragment->document->firstChild->nextSibling->firstChild->nextSibling;
}
if (! $fragment->root)
return false;
$fragment->isDocumentFragment = true;
return true;
}
protected function documentFragmentToMarkup($fragment) {
phpQuery::debug('documentFragmentToMarkup');
$tmp = $fragment->isDocumentFragment;
$fragment->isDocumentFragment = false;
$markup = $fragment->markup();
if ($fragment->isXML) {
$markup = substr($markup, 0, strrpos($markup, '</fake>'));
if ($fragment->isXHTML) {
$markup = substr($markup, strpos($markup, '<fake')+43);
} else {
$markup = substr($markup, strpos($markup, '<fake>')+6);
}
} else {
$markup = substr($markup, strpos($markup, '<body>')+6);
$markup = substr($markup, 0, strrpos($markup, '</body>'));
}
$fragment->isDocumentFragment = $tmp;
if (phpQuery::$debug)
phpQuery::debug('documentFragmentToMarkup: '.substr($markup, 0, 150));
return $markup;
}
/**
* Return document markup, starting with optional $nodes as root.
*
* @param $nodes DOMNode|DOMNodeList
* @return string
*/
public function markup($nodes = null, $innerMarkup = false) {
if (isset($nodes) && count($nodes) == 1 && $nodes[0] instanceof DOMDOCUMENT)
$nodes = null;
if (isset($nodes)) {
$markup = '';
if (!is_array($nodes) && !($nodes instanceof DOMNODELIST) )
$nodes = array($nodes);
if ($this->isDocumentFragment && ! $innerMarkup)
foreach($nodes as $i => $node)
if ($node->isSameNode($this->root)) {
// var_dump($node);
$nodes = array_slice($nodes, 0, $i)
+ phpQuery::DOMNodeListToArray($node->childNodes)
+ array_slice($nodes, $i+1);
}
if ($this->isXML && ! $innerMarkup) {
self::debug("Getting outerXML with charset '{$this->charset}'");
// we need outerXML, so we can benefit from
// $node param support in saveXML()
foreach($nodes as $node)
$markup .= $this->document->saveXML($node);
} else {
$loop = array();
if ($innerMarkup)
foreach($nodes as $node) {
if ($node->childNodes)
foreach($node->childNodes as $child)
$loop[] = $child;
else
$loop[] = $node;
}
else
$loop = $nodes;
self::debug("Getting markup, moving selected nodes (".count($loop).") to new DocumentFragment");
$fake = $this->documentFragmentCreate($loop);
$markup = $this->documentFragmentToMarkup($fake);
}
if ($this->isXHTML) {
self::debug("Fixing XHTML");
$markup = self::markupFixXHTML($markup);
}
self::debug("Markup: ".substr($markup, 0, 250));
return $markup;
} else {
if ($this->isDocumentFragment) {
// documentFragment, html only...
self::debug("Getting markup, DocumentFragment detected");
// return $this->markup(
//// $this->document->getElementsByTagName('body')->item(0)
// $this->document->root, true
// );
$markup = $this->documentFragmentToMarkup($this);
// no need for markupFixXHTML, as it's done thought markup($nodes) method
return $markup;
} else {
self::debug("Getting markup (".($this->isXML?'XML':'HTML')."), final with charset '{$this->charset}'");
$markup = $this->isXML
? $this->document->saveXML()
: $this->document->saveHTML();
if ($this->isXHTML) {
self::debug("Fixing XHTML");
$markup = self::markupFixXHTML($markup);
}
self::debug("Markup: ".substr($markup, 0, 250));
return $markup;
}
}
}
protected static function markupFixXHTML($markup) {
$markup = self::expandEmptyTag('script', $markup);
$markup = self::expandEmptyTag('select', $markup);
$markup = self::expandEmptyTag('textarea', $markup);
return $markup;
}
public static function debug($text) {
phpQuery::debug($text);
}
/**
* expandEmptyTag
*
* @param $tag
* @param $xml
* @return string
* @author mjaque at ilkebenson dot com
* @link http://php.net/manual/en/domdocument.savehtml.php#81256
*/
public static function expandEmptyTag($tag, $xml){
$indice = 0;
while ($indice< strlen($xml)){
$pos = strpos($xml, "<$tag ", $indice);
if ($pos){
$posCierre = strpos($xml, ">", $pos);
if ($xml[$posCierre-1] == "/"){
$xml = substr_replace($xml, "></$tag>", $posCierre-1, 2);
}
$indice = $posCierre;
}
else break;
}
return $xml;
}
}

View file

@ -0,0 +1,107 @@
<?php
/**
* DOMEvent class.
*
* Based on
* @link http://developer.mozilla.org/En/DOM:event
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
* @todo implement ArrayAccess ?
*/
class DOMEvent {
/**
* Returns a boolean indicating whether the event bubbles up through the DOM or not.
*
* @var unknown_type
*/
public $bubbles = true;
/**
* Returns a boolean indicating whether the event is cancelable.
*
* @var unknown_type
*/
public $cancelable = true;
/**
* Returns a reference to the currently registered target for the event.
*
* @var unknown_type
*/
public $currentTarget;
/**
* Returns detail about the event, depending on the type of event.
*
* @var unknown_type
* @link http://developer.mozilla.org/en/DOM/event.detail
*/
public $detail; // ???
/**
* Used to indicate which phase of the event flow is currently being evaluated.
*
* NOT IMPLEMENTED
*
* @var unknown_type
* @link http://developer.mozilla.org/en/DOM/event.eventPhase
*/
public $eventPhase; // ???
/**
* The explicit original target of the event (Mozilla-specific).
*
* NOT IMPLEMENTED
*
* @var unknown_type
*/
public $explicitOriginalTarget; // moz only
/**
* The original target of the event, before any retargetings (Mozilla-specific).
*
* NOT IMPLEMENTED
*
* @var unknown_type
*/
public $originalTarget; // moz only
/**
* Identifies a secondary target for the event.
*
* @var unknown_type
*/
public $relatedTarget;
/**
* Returns a reference to the target to which the event was originally dispatched.
*
* @var unknown_type
*/
public $target;
/**
* Returns the time that the event was created.
*
* @var unknown_type
*/
public $timeStamp;
/**
* Returns the name of the event (case-insensitive).
*/
public $type;
public $runDefault = true;
public $data = null;
public function __construct($data) {
foreach($data as $k => $v) {
$this->$k = $v;
}
if (! $this->timeStamp)
$this->timeStamp = time();
}
/**
* Cancels the event (if it is cancelable).
*
*/
public function preventDefault() {
$this->runDefault = false;
}
/**
* Stops the propagation of events further along in the DOM.
*
*/
public function stopPropagation() {
$this->bubbles = false;
}
}

View file

@ -0,0 +1,14 @@
<?php
/**
* Example of phpQuery bootstrap file.
*
* This file is executed everytime phpQuery is included. Use it to set all
* your personal needs in the library.
*
* To activate this file, delete '.example' from filename.
*/
// probably you want to use one of those functions here
//phpQuery::ajaxAllowHost();
//phpQuery::ajaxAllowURL();
//phpQuery::plugin();
?>

View file

@ -0,0 +1,88 @@
<?php
// -- Multibyte Compatibility functions ---------------------------------------
// http://svn.iphonewebdev.com/lace/lib/mb_compat.php
/**
* mb_internal_encoding()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_internal_encoding'))
{
function mb_internal_encoding($enc) {return true; }
}
/**
* mb_regex_encoding()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_regex_encoding'))
{
function mb_regex_encoding($enc) {return true; }
}
/**
* mb_strlen()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_strlen'))
{
function mb_strlen($str)
{
return strlen($str);
}
}
/**
* mb_strpos()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_strpos'))
{
function mb_strpos($haystack, $needle, $offset=0)
{
return strpos($haystack, $needle, $offset);
}
}
/**
* mb_stripos()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_stripos'))
{
function mb_stripos($haystack, $needle, $offset=0)
{
return stripos($haystack, $needle, $offset);
}
}
/**
* mb_substr()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_substr'))
{
function mb_substr($str, $start, $length=0)
{
return substr($str, $start, $length);
}
}
/**
* mb_substr_count()
*
* Included for mbstring pseudo-compatability.
*/
if (!function_exists('mb_substr_count'))
{
function mb_substr_count($haystack, $needle)
{
return substr_count($haystack, $needle);
}
}

View file

@ -0,0 +1,158 @@
<?php
/**
* Event handling class.
*
* @author Tobiasz Cudnik
* @package phpQuery
* @static
*/
abstract class phpQueryEvents {
/**
* Trigger a type of event on every matched element.
*
* @param DOMNode|phpQueryObject|string $document
* @param unknown_type $type
* @param unknown_type $data
*
* @TODO exclusive events (with !)
* @TODO global events (test)
* @TODO support more than event in $type (space-separated)
*/
public static function trigger($document, $type, $data = array(), $node = null) {
// trigger: function(type, data, elem, donative, extra) {
$documentID = phpQuery::getDocumentID($document);
$namespace = null;
if (strpos($type, '.') !== false)
list($name, $namespace) = explode('.', $type);
else
$name = $type;
if (! $node) {
if (self::issetGlobal($documentID, $type)) {
$pq = phpQuery::getDocument($documentID);
// TODO check add($pq->document)
$pq->find('*')->add($pq->document)
->trigger($type, $data);
}
} else {
if (isset($data[0]) && $data[0] instanceof DOMEvent) {
$event = $data[0];
$event->relatedTarget = $event->target;
$event->target = $node;
$data = array_slice($data, 1);
} else {
$event = new DOMEvent(array(
'type' => $type,
'target' => $node,
'timeStamp' => time(),
));
}
$i = 0;
while($node) {
// TODO whois
phpQuery::debug("Triggering ".($i?"bubbled ":'')."event '{$type}' on "
."node \n");//.phpQueryObject::whois($node)."\n");
$event->currentTarget = $node;
$eventNode = self::getNode($documentID, $node);
if (isset($eventNode->eventHandlers)) {
foreach($eventNode->eventHandlers as $eventType => $handlers) {
$eventNamespace = null;
if (strpos($type, '.') !== false)
list($eventName, $eventNamespace) = explode('.', $eventType);
else
$eventName = $eventType;
if ($name != $eventName)
continue;
if ($namespace && $eventNamespace && $namespace != $eventNamespace)
continue;
foreach($handlers as $handler) {
phpQuery::debug("Calling event handler\n");
$event->data = $handler['data']
? $handler['data']
: null;
$params = array_merge(array($event), $data);
$return = phpQuery::callbackRun($handler['callback'], $params);
if ($return === false) {
$event->bubbles = false;
}
}
}
}
// to bubble or not to bubble...
if (! $event->bubbles)
break;
$node = $node->parentNode;
$i++;
}
}
}
/**
* Binds a handler to one or more events (like click) for each matched element.
* Can also bind custom events.
*
* @param DOMNode|phpQueryObject|string $document
* @param unknown_type $type
* @param unknown_type $data Optional
* @param unknown_type $callback
*
* @TODO support '!' (exclusive) events
* @TODO support more than event in $type (space-separated)
* @TODO support binding to global events
*/
public static function add($document, $node, $type, $data, $callback = null) {
phpQuery::debug("Binding '$type' event");
$documentID = phpQuery::getDocumentID($document);
// if (is_null($callback) && is_callable($data)) {
// $callback = $data;
// $data = null;
// }
$eventNode = self::getNode($documentID, $node);
if (! $eventNode)
$eventNode = self::setNode($documentID, $node);
if (!isset($eventNode->eventHandlers[$type]))
$eventNode->eventHandlers[$type] = array();
$eventNode->eventHandlers[$type][] = array(
'callback' => $callback,
'data' => $data,
);
}
/**
* Enter description here...
*
* @param DOMNode|phpQueryObject|string $document
* @param unknown_type $type
* @param unknown_type $callback
*
* @TODO namespace events
* @TODO support more than event in $type (space-separated)
*/
public static function remove($document, $node, $type = null, $callback = null) {
$documentID = phpQuery::getDocumentID($document);
$eventNode = self::getNode($documentID, $node);
if (is_object($eventNode) && isset($eventNode->eventHandlers[$type])) {
if ($callback) {
foreach($eventNode->eventHandlers[$type] as $k => $handler)
if ($handler['callback'] == $callback)
unset($eventNode->eventHandlers[$type][$k]);
} else {
unset($eventNode->eventHandlers[$type]);
}
}
}
protected static function getNode($documentID, $node) {
foreach(phpQuery::$documents[$documentID]->eventsNodes as $eventNode) {
if ($node->isSameNode($eventNode))
return $eventNode;
}
}
protected static function setNode($documentID, $node) {
phpQuery::$documents[$documentID]->eventsNodes[] = $node;
return phpQuery::$documents[$documentID]->eventsNodes[
count(phpQuery::$documents[$documentID]->eventsNodes)-1
];
}
protected static function issetGlobal($documentID, $type) {
return isset(phpQuery::$documents[$documentID])
? in_array($type, phpQuery::$documents[$documentID]->eventsGlobal)
: false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
<?php
/**
* phpQuery plugin class extending phpQuery object.
* Methods from this class are callable on every phpQuery object.
*
* Class name prefix 'phpQueryObjectPlugin_' must be preserved.
*/
abstract class phpQueryObjectPlugin_Scripts {
/**
* Limit binded methods.
*
* null means all public.
* array means only specified ones.
*
* @var array|null
*/
public static $phpQueryMethods = null;
public static $config = array();
/**
* Enter description here...
*
* @param phpQueryObject $self
*/
public static function script($self, $arg1) {
$params = func_get_args();
$params = array_slice($params, 2);
$return = null;
$config = self::$config;
if (phpQueryPlugin_Scripts::$scriptMethods[$arg1]) {
phpQuery::callbackRun(
phpQueryPlugin_Scripts::$scriptMethods[$arg1],
array($self, $params, &$return, $config)
);
} else if ($arg1 != '__config' && file_exists(dirname(__FILE__)."/Scripts/$arg1.php")) {
phpQuery::debug("Loading script '$arg1'");
require dirname(__FILE__)."/Scripts/$arg1.php";
} else {
phpQuery::debug("Requested script '$arg1' doesn't exist");
}
return $return
? $return
: $self;
}
}
abstract class phpQueryPlugin_Scripts {
public static $scriptMethods = array();
public static function __initialize() {
if (file_exists(dirname(__FILE__)."/Scripts/__config.php")) {
include dirname(__FILE__)."/Scripts/__config.php";
phpQueryObjectPlugin_Scripts::$config = $config;
}
}
/**
* Extend scripts' namespace with $name related with $callback.
*
* Callback parameter order looks like this:
* - $this
* - $params
* - &$return
* - $config
*
* @param $name
* @param $callback
* @return bool
*/
public static function script($name, $callback) {
if (phpQueryPlugin_Scripts::$scriptMethods[$name])
throw new Exception("Script name conflict - '$name'");
phpQueryPlugin_Scripts::$scriptMethods[$name] = $callback;
}
}
?>

View file

@ -0,0 +1,10 @@
<?php
/**
* This file hosts config for Scripts plugin.
*
* To active this file, selete '.example' from filename.
*/
$config = array(
'google_login' => array('login@mail', 'password'),
);
?>

View file

@ -0,0 +1,14 @@
<?php
/**
* Example script for phpQuery Script plugin
*
* Avaible are 4 variables:
* - $self Represents $this
* - $params Represents parameters passed to script() method (without script name)
* - $return If not null, will be used as method result
* - $config Content of __config.php file
*
* By default each script returns $self aka $this.
*/
$return = $self->find($params[0]);
?>

View file

@ -0,0 +1,16 @@
<?php
$selector = 'img[src], link[href], script[src]';
$filter = ':not([href^=<?php])'
.':not([src^=<?php])'
.':not([href^=http://])'
.':not([src^=http://])'
.':not([src^=/])';
foreach($self[$selector]->filter($filter) as $el) {
$el = pq($el, $self->getDocumentID());
// imgs and scripts
if ( $el->is('img') || $el->is('script') )
$el->attr('src', $params[0].$el->attr('src'));
// css
if ( $el->is('link') )
$el->attr('href', $params[0].$el->attr('href'));
}

View file

@ -0,0 +1,47 @@
<?php
/**
* Automated google account login.
* Uses __config.php to keep login data.
*
* @package phpQuery.Plugins.Scripts
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
phpQuery::ajaxAllowHost(
'code.google.com',
'google.com', 'www.google.com',
'mail.google.com',
'docs.google.com',
'reader.google.com'
);
if (! function_exists('ndfasui8923')) {
function ndfasui8923($browser, $scope) {
extract($scope);
$browser
->WebBrowser()
->find('#Email')
->val($config['google_login'][0])->end()
->find('#Passwd')
->val($config['google_login'][1])
->parents('form')
->submit();
}
$ndfasui8923 = new Callback('ndfasui8923', new CallbackParam, compact(
'config', 'self', 'return', 'params'
));
}
phpQuery::plugin('WebBrowser');
$self->document->xhr = phpQuery::$plugins->browserGet(
'https://www.google.com/accounts/Login',
$ndfasui8923
);
//$self->document->xhr = phpQuery::$plugins->browserGet('https://www.google.com/accounts/Login', create_function('$browser', "
// \$browser
// ->WebBrowser()
// ->find('#Email')
// ->val('{$config['google_login'][0]}')->end()
// ->find('#Passwd')
// ->val('".str_replace("'", "\\'", $config['google_login'][1])."')
// ->parents('form')
// ->submit();"
//));
?>

View file

@ -0,0 +1,9 @@
<?php
/**
* Script outputs document markup and changes HTML special chars to entities.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
/** @var phpQueryObject */
$self = $self;
$return = htmlspecialchars($self);

View file

@ -0,0 +1,13 @@
<?php
/**
* Script makes content safe for printing as web page and not redirecting client.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
*/
/** @var phpQueryObject */
$self = $self;
$self
->find('script')
->add('meta[http-equiv=refresh]')
->add('meta[http-equiv=Refresh]')
->remove();

View file

@ -0,0 +1,436 @@
<?php
/**
* WebBrowser plugin.
*
*/
class phpQueryObjectPlugin_WebBrowser {
/**
* Limit binded methods to specified ones.
*
* @var array
*/
public static $phpQueryMethods = null;
/**
* Enter description here...
*
* @param phpQueryObject $self
* @todo support 'reset' event
*/
public static function WebBrowser($self, $callback = null, $location = null) {
$self = $self->_clone()->toRoot();
$location = $location
? $location
// TODO use document.location
: $self->document->xhr->getUri(true);
// FIXME tmp
$self->document->WebBrowserCallback = $callback;
if (! $location)
throw new Exception('Location needed to activate WebBrowser plugin !');
else {
$self->bind('click', array($location, $callback), array('phpQueryPlugin_WebBrowser', 'hadleClick'));
$self->bind('submit', array($location, $callback), array('phpQueryPlugin_WebBrowser', 'handleSubmit'));
}
}
public static function browser($self, $callback = null, $location = null) {
return $self->WebBrowser($callback, $location);
}
public static function downloadTo($self, $dir = null, $filename = null) {
$url = null;
if ($self->is('a[href]'))
$url = $self->attr('href');
else if ($self->find('a')->length)
$url = $self->find('a')->attr('href');
if ($url) {
$url = resolve_url($self->document->location, $url);
if (! $dir)
$dir = getcwd();
// TODO resolv name from response headers
if (! $filename) {
$matches = null;
preg_match('@/([^/]+)$@', $url, $matches);
$filename = $matches[1];
}
//print $url;
$path = rtrim($dir, '/').'/'.$filename;
phpQuery::debug("Requesting download of $url\n");
// TODO use AJAX instead of file_get_contents
file_put_contents($path, file_get_contents($url));
}
return $self;
}
/**
* Method changing browser location.
* Fires callback registered with WebBrowser(), if any.
* @param $self
* @param $url
* @return unknown_type
*/
public static function location($self, $url = null) {
// TODO if ! $url return actual location ???
$xhr = isset($self->document->xhr)
? $self->document->xhr
: null;
$xhr = phpQuery::ajax(array(
'url' => $url,
), $xhr);
$return = false;
if ($xhr->getLastResponse()->isSuccessful()) {
$return = phpQueryPlugin_WebBrowser::browserReceive($xhr);
if (isset($self->document->WebBrowserCallback))
phpQuery::callbackRun(
$self->document->WebBrowserCallback,
array($return)
);
}
return $return;
}
public static function download($self, $url = null) {
$xhr = isset($self->document->xhr)
? $self->document->xhr
: null;
$xhr = phpQuery::ajax(array(
'url' => $url,
), $xhr);
$return = false;
if ($xhr->getLastResponse()->isSuccessful()) {
$return = phpQueryPlugin_WebBrowser::browserDownload($xhr);
if (isset($self->document->WebBrowserCallback))
phpQuery::callbackRun(
$self->document->WebBrowserCallback,
array($return)
);
}
return $return;
}
}
class phpQueryPlugin_WebBrowser {
/**
*
* @param $url
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return Zend_Http_Client
*/
public static function browserGet($url, $callback,
$param1 = null, $param2 = null, $param3 = null) {
phpQuery::debug("[WebBrowser] GET: $url");
self::authorizeHost($url);
$xhr = phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'dataType' => 'html',
));
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
if ($xhr->getLastResponse()->isSuccessful()) {
phpQuery::callbackRun($callback,
array(self::browserReceive($xhr)->WebBrowser()),
$paramStructure
);
// phpQuery::callbackRun($callback, array(
// self::browserReceive($xhr)//->WebBrowser($callback)
// ));
return $xhr;
} else {
throw new Exception("[WebBrowser] GET request failed; url: $url");
return false;
}
}
/**
*
* @param $url
* @param $data
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return Zend_Http_Client
*/
public static function browserPost($url, $data, $callback,
$param1 = null, $param2 = null, $param3 = null) {
self::authorizeHost($url);
$xhr = phpQuery::ajax(array(
'type' => 'POST',
'url' => $url,
'dataType' => 'html',
'data' => $data,
));
$paramStructure = null;
if (func_num_args() > 3) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 3);
}
if ($xhr->getLastResponse()->isSuccessful()) {
phpQuery::callbackRun($callback,
array(self::browserReceive($xhr)->WebBrowser()),
$paramStructure
);
// phpQuery::callbackRun($callback, array(
// self::browserReceive($xhr)//->WebBrowser($callback)
// ));
return $xhr;
} else
return false;
}
/**
*
* @param $ajaxSettings
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return Zend_Http_Client
*/
public static function browser($ajaxSettings, $callback,
$param1 = null, $param2 = null, $param3 = null) {
self::authorizeHost($ajaxSettings['url']);
$xhr = phpQuery::ajax(
self::ajaxSettingsPrepare($ajaxSettings)
);
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
if ($xhr->getLastResponse()->isSuccessful()) {
phpQuery::callbackRun($callback,
array(self::browserReceive($xhr)->WebBrowser()),
$paramStructure
);
// phpQuery::callbackRun($callback, array(
// self::browserReceive($xhr)//->WebBrowser($callback)
// ));
return $xhr;
} else
return false;
}
protected static function authorizeHost($url) {
$host = parse_url($url, PHP_URL_HOST);
if ($host)
phpQuery::ajaxAllowHost($host);
}
protected static function ajaxSettingsPrepare($settings) {
unset($settings['success']);
unset($settings['error']);
return $settings;
}
/**
* @param Zend_Http_Client $xhr
*/
public static function browserReceive($xhr) {
phpQuery::debug("[WebBrowser] Received from ".$xhr->getUri(true));
// TODO handle meta redirects
$body = $xhr->getLastResponse()->getBody();
// XXX error ???
if (strpos($body, '<!doctype html>') !== false) {
$body = '<html>'
.str_replace('<!doctype html>', '', $body)
.'</html>';
}
$pq = phpQuery::newDocument($body);
$pq->document->xhr = $xhr;
$pq->document->location = $xhr->getUri(true);
$refresh = $pq->find('meta[http-equiv=refresh]')
->add('meta[http-equiv=Refresh]');
if ($refresh->size()) {
// print htmlspecialchars(var_export($xhr->getCookieJar()->getAllCookies(), true));
// print htmlspecialchars(var_export($xhr->getLastResponse()->getHeader('Set-Cookie'), true));
phpQuery::debug("Meta redirect... '{$refresh->attr('content')}'\n");
// there is a refresh, so get the new url
$content = $refresh->attr('content');
$urlRefresh = substr($content, strpos($content, '=')+1);
$urlRefresh = trim($urlRefresh, '\'"');
// XXX not secure ?!
phpQuery::ajaxAllowURL($urlRefresh);
// $urlRefresh = urldecode($urlRefresh);
// make ajax call, passing last $xhr object to preserve important stuff
$xhr = phpQuery::ajax(array(
'type' => 'GET',
'url' => $urlRefresh,
'dataType' => 'html',
), $xhr);
if ($xhr->getLastResponse()->isSuccessful()) {
// if all is ok, repeat this method...
return call_user_func_array(
array('phpQueryPlugin_WebBrowser', 'browserReceive'), array($xhr)
);
}
} else
return $pq;
}
/**
* @param Zend_Http_Client $xhr
*/
public static function browserDownload($xhr) {
phpQuery::debug("[WebBrowser] Received from ".$xhr->getUri(true));
// TODO handle meta redirects
$body = $xhr->getLastResponse()->getBody();
return $body;
}
/**
*
* @param $e
* @param $callback
* @return unknown_type
*/
public static function hadleClick($e, $callback = null) {
$node = phpQuery::pq($e->target);
$type = null;
if ($node->is('a[href]')) {
// TODO document.location
$xhr = isset($node->document->xhr)
? $node->document->xhr
: null;
$xhr = phpQuery::ajax(array(
'url' => resolve_url($e->data[0], $node->attr('href')),
'referer' => $node->document->location,
), $xhr);
if ((! $callback || !($callback instanceof Callback)) && $e->data[1])
$callback = $e->data[1];
if ($xhr->getLastResponse()->isSuccessful() && $callback)
phpQuery::callbackRun($callback, array(
self::browserReceive($xhr)
));
} else if ($node->is(':submit') && $node->parents('form')->size())
$node->parents('form')->trigger('submit', array($e));
}
/**
* Enter description here...
*
* @param unknown_type $e
* @TODO trigger submit for form after form's submit button has a click event
*/
public static function handleSubmit($e, $callback = null) {
$node = phpQuery::pq($e->target);
if (!$node->is('form') || !$node->is('[action]'))
return;
// TODO document.location
$xhr = isset($node->document->xhr)
? $node->document->xhr
: null;
$submit = pq($e->relatedTarget)->is(':submit')
? $e->relatedTarget
// will this work ?
// : $node->find(':submit:first')->get(0);
: $node->find('*:submit:first')->get(0);
$data = array();
foreach($node->serializeArray($submit) as $r)
// XXXt.c maybe $node->not(':submit')->add($sumit) would be better ?
// foreach($node->serializeArray($submit) as $r)
$data[ $r['name'] ] = $r['value'];
$options = array(
'type' => $node->attr('method')
? $node->attr('method')
: 'GET',
'url' => resolve_url($e->data[0], $node->attr('action')),
'data' => $data,
'referer' => $node->document->location,
// 'success' => $e->data[1],
);
if ($node->attr('enctype'))
$options['contentType'] = $node->attr('enctype');
$xhr = phpQuery::ajax($options, $xhr);
if ((! $callback || !($callback instanceof Callback)) && $e->data[1])
$callback = $e->data[1];
if ($xhr->getLastResponse()->isSuccessful() && $callback)
phpQuery::callbackRun($callback, array(
self::browserReceive($xhr)
));
}
}
/**
*
* @param unknown_type $parsed
* @return unknown
* @link http://www.php.net/manual/en/function.parse-url.php
* @author stevenlewis at hotmail dot com
*/
function glue_url($parsed)
{
if (! is_array($parsed)) return false;
$uri = isset($parsed['scheme']) ? $parsed['scheme'].':'.((strtolower($parsed['scheme']) == 'mailto') ? '':'//'): '';
$uri .= isset($parsed['user']) ? $parsed['user'].($parsed['pass']? ':'.$parsed['pass']:'').'@':'';
$uri .= isset($parsed['host']) ? $parsed['host'] : '';
$uri .= isset($parsed['port']) ? ':'.$parsed['port'] : '';
if(isset($parsed['path']))
{
$uri .= (substr($parsed['path'],0,1) == '/')?$parsed['path']:'/'.$parsed['path'];
}
$uri .= isset($parsed['query']) ? '?'.$parsed['query'] : '';
$uri .= isset($parsed['fragment']) ? '#'.$parsed['fragment'] : '';
return $uri;
}
/**
* Enter description here...
*
* @param unknown_type $base
* @param unknown_type $url
* @return unknown
* @author adrian-php at sixfingeredman dot net
*/
function resolve_url($base, $url) {
if (!strlen($base)) return $url;
// Step 2
if (!strlen($url)) return $base;
// Step 3
if (preg_match('!^[a-z]+:!i', $url)) return $url;
$base = parse_url($base);
if ($url{0} == "#") {
// Step 2 (fragment)
$base['fragment'] = substr($url, 1);
return unparse_url($base);
}
unset($base['fragment']);
unset($base['query']);
if (substr($url, 0, 2) == "//") {
// Step 4
return unparse_url(array(
'scheme'=>$base['scheme'],
'path'=>substr($url,2),
));
} else if ($url{0} == "/") {
// Step 5
$base['path'] = $url;
} else {
// Step 6
$path = explode('/', $base['path']);
$url_path = explode('/', $url);
// Step 6a: drop file from base
array_pop($path);
// Step 6b, 6c, 6e: append url while removing "." and ".." from
// the directory portion
$end = array_pop($url_path);
foreach ($url_path as $segment) {
if ($segment == '.') {
// skip
} else if ($segment == '..' && $path && $path[sizeof($path)-1] != '..') {
array_pop($path);
} else {
$path[] = $segment;
}
}
// Step 6d, 6f: remove "." and ".." from file portion
if ($end == '.') {
$path[] = '';
} else if ($end == '..' && $path && $path[sizeof($path)-1] != '..') {
$path[sizeof($path)-1] = '';
} else {
$path[] = $end;
}
// Step 6h
$base['path'] = join('/', $path);
}
// Step 7
return glue_url($base);
}

View file

@ -0,0 +1,75 @@
<?php
/**
* Example of phpQuery plugin.
*
* Load it like this:
* phpQuery::plugin('example')
* phpQuery::plugin('example', 'example.php')
* pq('ul')->plugin('example')
* pq('ul')->plugin('example', 'example.php')
*
* Plugin classes are never intialized, just method calls are forwarded
* in static way from phpQuery.
*
* Have fun writing plugins :)
*/
/**
* phpQuery plugin class extending phpQuery object.
* Methods from this class are callable on every phpQuery object.
*
* Class name prefix 'phpQueryObjectPlugin_' must be preserved.
*/
abstract class phpQueryObjectPlugin_example {
/**
* Limit binded methods.
*
* null means all public.
* array means only specified ones.
*
* @var array|null
*/
public static $phpQueryMethods = null;
/**
* Enter description here...
*
* @param phpQueryObject $self
*/
public static function example($self, $arg1) {
// this method can be called on any phpQuery object, like this:
// pq('div')->example('$arg1 Value')
// do something
$self->append('Im just an example !');
// change stack of result object
return $self->find('div');
}
protected static function helperFunction() {
// this method WONT be avaible as phpQuery method,
// because it isn't publicly callable
}
}
/**
* phpQuery plugin class extending phpQuery static namespace.
* Methods from this class are callable as follows:
* phpQuery::$plugins->staticMethod()
*
* Class name prefix 'phpQueryPlugin_' must be preserved.
*/
abstract class phpQueryPlugin_example {
/**
* Limit binded methods.
*
* null means all public.
* array means only specified ones.
*
* @var array|null
*/
public static $phpQueryMethods = null;
public static function staticMethod() {
// this method can be called within phpQuery class namespace, like this:
// phpQuery::$plugins->staticMethod()
}
}
?>