<?php
/**
* phpQuery is a server-side, chainable, CSS3 selector driven
* Document Object Model (DOM) API based on jQuery JavaScript Library.
*
* @version 0.9.5
* @link http://code.google.com/p/phpquery/
* @link http://phpquery-library.blogspot.com/
* @link http://jquery.com/
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @package phpQuery
*/
// class names for instanceof
// TODO move them as class constants into phpQuery
define('DOMDOCUMENT', 'DOMDocument');
define('DOMELEMENT', 'DOMElement');
define('DOMNODELIST', 'DOMNodeList');
define('DOMNODE', 'DOMNode');
require_once(dirname(__FILE__).'/phpQuery/DOMEvent.php');
require_once(dirname(__FILE__).'/phpQuery/DOMDocumentWrapper.php');
require_once(dirname(__FILE__).'/phpQuery/phpQueryEvents.php');
require_once(dirname(__FILE__).'/phpQuery/Callback.php');
require_once(dirname(__FILE__).'/phpQuery/phpQueryObject.php');
require_once(dirname(__FILE__).'/phpQuery/compat/mbstring.php');
/**
* Static namespace for phpQuery functions.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
abstract class phpQuery {
/**
* XXX: Workaround for mbstring problems
*
* @var bool
*/
public static $mbstringSupport = true;
public static $debug = false;
public static $documents = array();
public static $defaultDocumentID = null;
// public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
/**
* Applies only to HTML.
*
* @var unknown_type
*/
public static $defaultDoctype = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">';
public static $defaultCharset = 'UTF-8';
/**
* Static namespace for plugins.
*
* @var object
*/
public static $plugins = array();
/**
* List of loaded plugins.
*
* @var unknown_type
*/
public static $pluginsLoaded = array();
public static $pluginsMethods = array();
public static $pluginsStaticMethods = array();
public static $extendMethods = array();
/**
* @TODO implement
*/
public static $extendStaticMethods = array();
/**
* Hosts allowed for AJAX connections.
* Dot '.' means $_SERVER['HTTP_HOST'] (if any).
*
* @var array
*/
public static $ajaxAllowedHosts = array(
'.'
);
/**
* AJAX settings.
*
* @var array
* XXX should it be static or not ?
*/
public static $ajaxSettings = array(
'url' => '',//TODO
'global' => true,
'type' => "GET",
'timeout' => null,
'contentType' => "application/x-www-form-urlencoded",
'processData' => true,
// 'async' => true,
'data' => null,
'username' => null,
'password' => null,
'accepts' => array(
'xml' => "application/xml, text/xml",
'html' => "text/html",
'script' => "text/javascript, application/javascript",
'json' => "application/json, text/javascript",
'text' => "text/plain",
'_default' => "*/*"
)
);
public static $lastModified = null;
public static $active = 0;
public static $dumpCount = 0;
/**
* Multi-purpose function.
* Use pq() as shortcut.
*
* In below examples, $pq is any result of pq(); function.
*
* 1. Import markup into existing document (without any attaching):
* - Import into selected document:
* pq('<div/>') // DOESNT accept text nodes at beginning of input string !
* - Import into document with ID from $pq->getDocumentID():
* pq('<div/>', $pq->getDocumentID())
* - Import into same document as DOMNode belongs to:
* pq('<div/>', DOMNode)
* - Import into document from phpQuery object:
* pq('<div/>', $pq)
*
* 2. Run query:
* - Run query on last selected document:
* pq('div.myClass')
* - Run query on document with ID from $pq->getDocumentID():
* pq('div.myClass', $pq->getDocumentID())
* - Run query on same document as DOMNode belongs to and use node(s)as root for query:
* pq('div.myClass', DOMNode)
* - Run query on document from phpQuery object
* and use object's stack as root node(s) for query:
* pq('div.myClass', $pq)
*
* @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
* @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
*
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
* phpQuery object or false in case of error.
*/
public static function pq($arg1, $context = null) {
if ($arg1 instanceof DOMNODE && ! isset($context)) {
foreach(phpQuery::$documents as $documentWrapper) {
$compare = $arg1 instanceof DOMDocument
? $arg1 : $arg1->ownerDocument;
if ($documentWrapper->document->isSameNode($compare))
$context = $documentWrapper->id;
}
}
if (! $context) {
$domId = self::$defaultDocumentID;
if (! $domId)
throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
// } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
} else if (is_object($context) && $context instanceof phpQueryObject)
$domId = $context->getDocumentID();
else if ($context instanceof DOMDOCUMENT) {
$domId = self::getDocumentID($context);
if (! $domId) {
//throw new Exception('Orphaned DOMDocument');
$domId = self::newDocument($context)->getDocumentID();
}
} else if ($context instanceof DOMNODE) {
$domId = self::getDocumentID($context);
if (! $domId) {
throw new Exception('Orphaned DOMNode');
// $domId = self::newDocument($context->ownerDocument);
}
} else
$domId = $context;
if ($arg1 instanceof phpQueryObject) {
// if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
/**
* Return $arg1 or import $arg1 stack if document differs:
* pq(pq('<div/>'))
*/
if ($arg1->getDocumentID() == $domId)
return $arg1;
$class = get_class($arg1);
// support inheritance by passing old object to overloaded constructor
$phpQuery = $class != 'phpQuery'
? new $class($arg1, $domId)
: new phpQueryObject($domId);
$phpQuery->elements = array();
foreach($arg1->elements as $node)
$phpQuery->elements[] = $phpQuery->document->importNode($node, true);
return $phpQuery;
} else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
/*
* Wrap DOM nodes with phpQuery object, import into document when needed:
* pq(array($domNode1, $domNode2))
*/
$phpQuery = new phpQueryObject($domId);
if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
$arg1 = array($arg1);
$phpQuery->elements = array();
foreach($arg1 as $node) {
$sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
&& ! $node->ownerDocument->isSameNode($phpQuery->document);
$phpQuery->elements[] = $sameDocument
? $phpQuery->document->importNode($node, true)
: $node;
}
return $phpQuery;
} else if (self::isMarkup($arg1)) {
/**
* Import HTML:
* pq('<div/>')
*/
$phpQuery = new phpQueryObject($domId);
return $phpQuery->newInstance(
$phpQuery->documentWrapper->import($arg1)
);
} else {
/**
* Run CSS query:
* pq('div.myClass')
*/
$phpQuery = new phpQueryObject($domId);
// if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
if ($context && $context instanceof phpQueryObject)
$phpQuery->elements = $context->elements;
else if ($context && $context instanceof DOMNODELIST) {
$phpQuery->elements = array();
foreach($context as $node)
$phpQuery->elements[] = $node;
} else if ($context && $context instanceof DOMNODE)
$phpQuery->elements = array($context);
return $phpQuery->find($arg1);
}
}
/**
* Sets default document to $id. Document has to be loaded prior
* to using this method.
* $id can be retrived via getDocumentID() or getDocumentIDRef().
*
* @param unknown_type $id
*/
public static function selectDocument($id) {
$id = self::getDocumentID($id);
self::debug("Selecting document '$id' as default one");
self::$defaultDocumentID = self::getDocumentID($id);
}
/**
* Returns document with id $id or last used as phpQueryObject.
* $id can be retrived via getDocumentID() or getDocumentIDRef().
* Chainable.
*
* @see phpQuery::selectDocument()
* @param unknown_type $id
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function getDocument($id = null) {
if ($id)
phpQuery::selectDocument($id);
else
$id = phpQuery::$defaultDocumentID;
return new phpQueryObject($id);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocument($markup = null, $contentType = null) {
if (! $markup)
$markup = '';
$documentID = phpQuery::createDocumentWrapper($markup, $contentType);
return new phpQueryObject($documentID);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentHTML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "text/html{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentXML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "text/xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentXHTML($markup = null, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocument($markup, "application/xhtml+xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentPHP($markup = null, $contentType = "text/html") {
// TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
$markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
return self::newDocument($markup, $contentType);
}
public static function phpToMarkup($php, $charset = 'utf-8') {
$regexes = array(
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
);
foreach($regexes as $regex)
while (preg_match($regex, $php, $matches)) {
$php = preg_replace_callback(
$regex,
// create_function('$m, $charset = "'.$charset.'"',
// 'return $m[1].$m[2]
// .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
// .$m[5].$m[2];'
// ),
array('phpQuery', '_phpToMarkupCallback'),
$php
);
}
$regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
//preg_match_all($regex, $php, $matches);
//var_dump($matches);
$php = preg_replace($regex, '\\1<php><!-- \\3 --></php>', $php);
return $php;
}
public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
return $m[1].$m[2]
.htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
.$m[5].$m[2];
}
public static function _markupToPHPCallback($m) {
return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
}
/**
* Converts document markup containing PHP code generated by phpQuery::php()
* into valid (executable) PHP code syntax.
*
* @param string|phpQueryObject $content
* @return string PHP code.
*/
public static function markupToPHP($content) {
if ($content instanceof phpQueryObject)
$content = $content->markupOuter();
/* <php>...</php> to <?php...? > */
$content = preg_replace_callback(
'@<php>\s*<!--(.*?)-->\s*</php>@s',
// create_function('$m',
// 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
// ),
array('phpQuery', '_markupToPHPCallback'),
$content
);
/* <node attr='< ?php ? >'> extra space added to save highlighters */
$regexes = array(
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:<|%3C)\\?(?:php)?(.*?)(?:\\?(?:>|%3E))([^\']*)\'@s',
'@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:<|%3C)\\?(?:php)?(.*?)(?:\\?(?:>|%3E))([^"]*)"@s',
);
foreach($regexes as $regex)
while (preg_match($regex, $content))
$content = preg_replace_callback(
$regex,
create_function('$m',
'return $m[1].$m[2].$m[3]."<?php "
.str_replace(
array("%20", "%3E", "%09", " ", "	", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
array(" ", ">", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
htmlspecialchars_decode($m[4])
)
." ?>".$m[5].$m[2];'
),
$content
);
return $content;
}
/**
* Creates new document from file $file.
* Chainable.
*
* @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFile($file, $contentType = null) {
$documentID = self::createDocumentWrapper(
file_get_contents($file), $contentType
);
return new phpQueryObject($documentID);
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileHTML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "text/html{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileXML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "text/xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFileXHTML($file, $charset = null) {
$contentType = $charset
? ";charset=$charset"
: '';
return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
}
/**
* Creates new document from markup.
* Chainable.
*
* @param unknown_type $markup
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
*/
public static function newDocumentFilePHP($file, $contentType = null) {
return self::newDocumentPHP(file_get_contents($file), $contentType);
}
/**
* Reuses existing DOMDocument object.
* Chainable.
*
* @param $document DOMDocument
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
* @TODO support DOMDocument
*/
public static function loadDocument($document) {
// TODO
die('TODO loadDocument');
}
/**
* Enter description here...
*
* @param unknown_type $html
* @param unknown_type $domId
* @return unknown New DOM ID
* @todo support PHP tags in input
* @todo support passing DOMDocument object from self::loadDocument
*/
protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
if (function_exists('domxml_open_mem'))
throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
// $id = $documentID
// ? $documentID
// : md5(microtime());
$document = null;
if ($html instanceof DOMDOCUMENT) {
if (self::getDocumentID($html)) {
// document already exists in phpQuery::$documents, make a copy
$document = clone $html;
} else {
// new document, add it to phpQuery::$documents
$wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
}
} else {
$wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
}
// $wrapper->id = $id;
// bind document
phpQuery::$documents[$wrapper->id] = $wrapper;
// remember last loaded document
phpQuery::selectDocument($wrapper->id);
return $wrapper->id;
}
/**
* Extend class namespace.
*
* @param string|array $target
* @param array $source
* @TODO support string $source
* @return unknown_type
*/
public static function extend($target, $source) {
switch($target) {
case 'phpQueryObject':
$targetRef = &self::$extendMethods;
$targetRef2 = &self::$pluginsMethods;
break;
case 'phpQuery':
$targetRef = &self::$extendStaticMethods;
$targetRef2 = &self::$pluginsStaticMethods;
break;
default:
throw new Exception("Unsupported \$target type");
}
if (is_string($source))
$source = array($source => $source);
foreach($source as $method => $callback) {
if (isset($targetRef[$method])) {
// throw new Exception
self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
continue;
}
if (isset($targetRef2[$method])) {
// throw new Exception
self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
." can\'t extend '{$target}'");
continue;
}
$targetRef[$method] = $callback;
}
return true;
}
/**
* Extend phpQuery with $class from $file.
*
* @param string $class Extending class name. Real class name can be prepended phpQuery_.
* @param string $file Filename to include. Defaults to "{$class}.php".
*/
public static function plugin($class, $file = null) {
// TODO $class checked agains phpQuery_$class
// if (strpos($class, 'phpQuery') === 0)
// $class = substr($class, 8);
if (in_array($class, self::$pluginsLoaded))
return true;
if (! $file)
$file = $class.'.php';
$objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
$staticClassExists = class_exists('phpQueryPlugin_'.$class);
if (! $objectClassExists && ! $staticClassExists)
require_once($file);
self::$pluginsLoaded[] = $class;
// static methods
if (class_exists('phpQueryPlugin_'.$class)) {
$realClass = 'phpQueryPlugin_'.$class;
$vars = get_class_vars($realClass);
$loop = isset($vars['phpQueryMethods'])
&& ! is_null($vars['phpQueryMethods'])
? $vars['phpQueryMethods']
: get_class_methods($realClass);
foreach($loop as $method) {
if ($method == '__initialize')
continue;
if (! is_callable(array($realClass, $method)))
continue;
if (isset(self::$pluginsStaticMethods[$method])) {
throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
return;
}
self::$pluginsStaticMethods[$method] = $class;
}
if (method_exists($realClass, '__initialize'))
call_user_func_array(array($realClass, '__initialize'), array());
}
// object methods
if (class_exists('phpQueryObjectPlugin_'.$class)) {
$realClass = 'phpQueryObjectPlugin_'.$class;
$vars = get_class_vars($realClass);
$loop = isset($vars['phpQueryMethods'])
&& ! is_null($vars['phpQueryMethods'])
? $vars['phpQueryMethods']
: get_class_methods($realClass);
foreach($loop as $method) {
if (! is_callable(array($realClass, $method)))
continue;
if (isset(self::$pluginsMethods[$method])) {
throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
continue;
}
self::$pluginsMethods[$method] = $class;
}
}
return true;
}
/**
* Unloades all or specified document from memory.
*
* @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
*/
public static function unloadDocuments($id = null) {
if (isset($id)) {
if ($id = self::getDocumentID($id))
unset(phpQuery::$documents[$id]);
} else {
foreach(phpQuery::$documents as $k => $v) {
unset(phpQuery::$documents[$k]);
}
}
}
/**
* Parses phpQuery object or HTML result against PHP tags and makes them active.
*
* @param phpQuery|string $content
* @deprecated
* @return string
*/
public static function unsafePHPTags($content) {
return self::markupToPHP($content);
}
public static function DOMNodeListToArray($DOMNodeList) {
$array = array();
if (! $DOMNodeList)
return $array;
foreach($DOMNodeList as $node)
$array[] = $node;
return $array;
}
/**
* Checks if $input is HTML string, which has to start with '<'.
*
* @deprecated
* @param String $input
* @return Bool
* @todo still used ?
*/
public static function isMarkup($input) {
return ! is_array($input) && substr(trim($input), 0, 1) == '<';
}
public static function debug($text) {
if (self::$debug)
print var_dump($text);
}
/**
* Make an AJAX request.
*
* @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
* Additional options are:
* 'document' - document for global events, @see phpQuery::getDocumentID()
* 'referer' - implemented
* 'requested_with' - TODO; not implemented (X-Requested-With)
* @return Zend_Http_Client
* @link http://docs.jquery.com/Ajax/jQuery.ajax
*
* @TODO $options['cache']
* @TODO $options['processData']
* @TODO $options['xhr']
* @TODO $options['data'] as string
* @TODO XHR interface
*/
public static function ajax($options = array(), $xhr = null) {
$options = array_merge(
self::$ajaxSettings, $options
);
$documentID = isset($options['document'])
? self::getDocumentID($options['document'])
: null;
if ($xhr) {
// reuse existing XHR object, but clean it up
$client = $xhr;
// $client->setParameterPost(null);
// $client->setParameterGet(null);
$client->setAuth(false);
$client->setHeaders("If-Modified-Since", null);
$client->setHeaders("Referer", null);
$client->resetParameters();
} else {
// create new XHR object
require_once('Zend/Http/Client.php');
$client = new Zend_Http_Client();
$client->setCookieJar();
}
if (isset($options['timeout']))
$client->setConfig(array(
'timeout' => $options['timeout'],
));
// 'maxredirects' => 0,
foreach(self::$ajaxAllowedHosts as $k => $host)
if ($host == '.' && isset($_SERVER['HTTP_HOST']))
self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
$host = parse_url($options['url'], PHP_URL_HOST);
if (! in_array($host, self::$ajaxAllowedHosts)) {
throw new Exception("Request not permitted, host '$host' not present in "
."phpQuery::\$ajaxAllowedHosts");
}
// JSONP
$jsre = "/=\\?(&|$)/";
if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
$jsonpCallbackParam = $options['jsonp']
? $options['jsonp'] : 'callback';
if (strtolower($options['type']) == 'get') {
if (! preg_match($jsre, $options['url'])) {
$sep = strpos($options['url'], '?')
? '&' : '?';
$options['url'] .= "$sep$jsonpCallbackParam=?";
}
} else if ($options['data']) {
$jsonp = false;
foreach($options['data'] as $n => $v) {
if ($v == '?')
$jsonp = true;
}
if (! $jsonp) {
$options['data'][$jsonpCallbackParam] = '?';
}
}
$options['dataType'] = 'json';
}
if (isset($options['dataType']) && $options['dataType'] == 'json') {
$jsonpCallback = 'json_'.md5(microtime());
$jsonpData = $jsonpUrl = false;
if ($options['data']) {
foreach($options['data'] as $n => $v) {
if ($v == '?')
$jsonpData = $n;
}
}
if (preg_match($jsre, $options['url']))
$jsonpUrl = true;
if ($jsonpData !== false || $jsonpUrl) {
// remember callback name for httpData()
$options['_jsonp'] = $jsonpCallback;
if ($jsonpData !== false)
$options['data'][$jsonpData] = $jsonpCallback;
if ($jsonpUrl)
$options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
}
}
$client->setUri($options['url']);
$client->setMethod(strtoupper($options['type']));
if (isset($options['referer']) && $options['referer'])
$client->setHeaders('Referer', $options['referer']);
$client->setHeaders(array(
// 'content-type' => $options['contentType'],
'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
.'/2008122010 Firefox/3.0.5',
// TODO custom charset
'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
// 'Connection' => 'keep-alive',
// 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' => 'en-us,en;q=0.5',
));
if ($options['username'])
$client->setAuth($options['username'], $options['password']);
if (isset($options['ifModified']) && $options['ifModified'])
$client->setHeaders("If-Modified-Since",
self::$lastModified
? self::$lastModified
: "Thu, 01 Jan 1970 00:00:00 GMT"
);
$client->setHeaders("Accept",
isset($options['dataType'])
&& isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
: self::$ajaxSettings['accepts']['_default']
);
// TODO $options['processData']
if ($options['data'] instanceof phpQueryObject) {
$serialized = $options['data']->serializeArray($options['data']);
$options['data'] = array();
foreach($serialized as $r)
$options['data'][ $r['name'] ] = $r['value'];
}
if (strtolower($options['type']) == 'get') {
$client->setParameterGet($options['data']);
} else if (strtolower($options['type']) == 'post') {
$client->setEncType($options['contentType']);
$client->setParameterPost($options['data']);
}
if (self::$active == 0 && $options['global'])
phpQueryEvents::trigger($documentID, 'ajaxStart');
self::$active++;
// beforeSend callback
if (isset($options['beforeSend']) && $options['beforeSend'])
phpQuery::callbackRun($options['beforeSend'], array($client));
// ajaxSend event
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
if (phpQuery::$debug) {
self::debug("{$options['type']}: {$options['url']}\n");
self::debug("Options: <pre>".var_export($options, true)."</pre>\n");
// if ($client->getCookieJar())
// self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\n");
}
// request
$response = $client->request();
if (phpQuery::$debug) {
self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
self::debug($client->getLastRequest());
self::debug($response->getHeaders());
}
if ($response->isSuccessful()) {
// XXX tempolary
self::$lastModified = $response->getHeader('Last-Modified');
$data = self::httpData($response->getBody(), $options['dataType'], $options);
if (isset($options['success']) && $options['success'])
phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
} else {
if (isset($options['error']) && $options['error'])
phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
}
if (isset($options['complete']) && $options['complete'])
phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
if ($options['global'])
phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
if ($options['global'] && ! --self::$active)
phpQueryEvents::trigger($documentID, 'ajaxStop');
return $client;
// if (is_null($domId))
// $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
// return new phpQueryAjaxResponse($response, $domId);
}
protected static function httpData($data, $type, $options) {
if (isset($options['dataFilter']) && $options['dataFilter'])
$data = self::callbackRun($options['dataFilter'], array($data, $type));
if (is_string($data)) {
if ($type == "json") {
if (isset($options['_jsonp']) && $options['_jsonp']) {
$data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
}
$data = self::parseJSON($data);
}
}
return $data;
}
/**
* Enter description here...
*
* @param array|phpQuery $data
*
*/
public static function param($data) {
return http_build_query($data, null, '&');
}
public static function get($url, $data = null, $callback = null, $type = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
// TODO some array_values on this shit
return phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => $type,
));
}
public static function post($url, $data = null, $callback = null, $type = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
return phpQuery::ajax(array(
'type' => 'POST',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => $type,
));
}
public static function getJSON($url, $data = null, $callback = null) {
if (!is_array($data)) {
$callback = $data;
$data = null;
}
// TODO some array_values on this shit
return phpQuery::ajax(array(
'type' => 'GET',
'url' => $url,
'data' => $data,
'success' => $callback,
'dataType' => 'json',
));
}
public static function ajaxSetup($options) {
self::$ajaxSettings = array_merge(
self::$ajaxSettings,
$options
);
}
public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
$loop = is_array($host1)
? $host1
: func_get_args();
foreach($loop as $host) {
if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
phpQuery::$ajaxAllowedHosts[] = $host;
}
}
}
public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
$loop = is_array($url1)
? $url1
: func_get_args();
foreach($loop as $url)
phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
}
/**
* Returns JSON representation of $data.
*
* @static
* @param mixed $data
* @return string
*/
public static function toJSON($data) {
if (function_exists('json_encode'))
return json_encode($data);
require_once('Zend/Json/Encoder.php');
return Zend_Json_Encoder::encode($data);
}
/**
* Parses JSON into proper PHP type.
*
* @static
* @param string $json
* @return mixed
*/
public static function parseJSON($json) {
if (function_exists('json_decode')) {
$return = json_decode(trim($json), true);
// json_decode and UTF8 issues
if (isset($return))
return $return;
}
require_once('Zend/Json/Decoder.php');
return Zend_Json_Decoder::decode($json);
}
/**
* Returns source's document ID.
*
* @param $source DOMNode|phpQueryObject
* @return string
*/
public static function getDocumentID($source) {
if ($source instanceof DOMDOCUMENT) {
foreach(phpQuery::$documents as $id => $document) {
if ($source->isSameNode($document->document))
return $id;
}
} else if ($source instanceof DOMNODE) {
foreach(phpQuery::$documents as $id => $document) {
if ($source->ownerDocument->isSameNode($document->document))
return $id;
}
} else if ($source instanceof phpQueryObject)
return $source->getDocumentID();
else if (is_string($source) && isset(phpQuery::$documents[$source]))
return $source;
}
/**
* Get DOMDocument object related to $source.
* Returns null if such document doesn't exist.
*
* @param $source DOMNode|phpQueryObject|string
* @return string
*/
public static function getDOMDocument($source) {
if ($source instanceof DOMDOCUMENT)
return $source;
$source = self::getDocumentID($source);
return $source
? self::$documents[$id]['document']
: null;
}
// UTILITIES
// http://docs.jquery.com/Utilities
/**
*
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.makeArray
*/
public static function makeArray($obj) {
$array = array();
if (is_object($object) && $object instanceof DOMNODELIST) {
foreach($object as $value)
$array[] = $value;
} else if (is_object($object) && ! ($object instanceof Iterator)) {
foreach(get_object_vars($object) as $name => $value)
$array[0][$name] = $value;
} else {
foreach($object as $name => $value)
$array[0][$name] = $value;
}
return $array;
}
public static function inArray($value, $array) {
return in_array($value, $array);
}
/**
*
* @param $object
* @param $callback
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.each
*/
public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
if (is_object($object) && ! ($object instanceof Iterator)) {
foreach(get_object_vars($object) as $name => $value)
phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
} else {
foreach($object as $name => $value)
phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
}
}
/**
*
* @link http://docs.jquery.com/Utilities/jQuery.map
*/
public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
$result = array();
$paramStructure = null;
if (func_num_args() > 2) {
$paramStructure = func_get_args();
$paramStructure = array_slice($paramStructure, 2);
}
foreach($array as $v) {
$vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
// $callbackArgs = $args;
// foreach($args as $i => $arg) {
// $callbackArgs[$i] = $arg instanceof CallbackParam
// ? $v
// : $arg;
// }
// $vv = call_user_func_array($callback, $callbackArgs);
if (is_array($vv)) {
foreach($vv as $vvv)
$result[] = $vvv;
} else if ($vv !== null) {
$result[] = $vv;
}
}
return $result;
}
/**
*
* @param $callback Callback
* @param $params
* @param $paramStructure
* @return unknown_type
*/
public static function callbackRun($callback, $params = array(), $paramStructure = null) {
if (! $callback)
return;
if ($callback instanceof CallbackParameterToReference) {
// TODO support ParamStructure to select which $param push to reference
if (isset($params[0]))
$callback->callback = $params[0];
return true;
}
if ($callback instanceof Callback) {
$paramStructure = $callback->params;
$callback = $callback->callback;
}
if (! $paramStructure)
return call_user_func_array($callback, $params);
$p = 0;
foreach($paramStructure as $i => $v) {
$paramStructure[$i] = $v instanceof CallbackParam
? $params[$p++]
: $v;
}
return call_user_func_array($callback, $paramStructure);
}
/**
* Merge 2 phpQuery objects.
* @param array $one
* @param array $two
* @protected
* @todo node lists, phpQueryObject
*/
public static function merge($one, $two) {
$elements = $one->elements;
foreach($two->elements as $node) {
$exists = false;
foreach($elements as $node2) {
if ($node2->isSameNode($node))
$exists = true;
}
if (! $exists)
$elements[] = $node;
}
return $elements;
// $one = $one->newInstance();
// $one->elements = $elements;
// return $one;
}
/**
*
* @param $array
* @param $callback
* @param $invert
* @return unknown_type
* @link http://docs.jquery.com/Utilities/jQuery.grep
*/
public static function grep($array, $callback, $invert = false) {
$result = array();
foreach($array as $k => $v) {
$r = call_user_func_array($callback, array($v, $k));
if ($r === !(bool)$invert)
$result[] = $v;
}
return $result;
}
public static function unique($array) {
return array_unique($array);
}
/**
*
* @param $function
* @return unknown_type
* @TODO there are problems with non-static methods, second parameter pass it
* but doesnt verify is method is really callable
*/
public static function isFunction($function) {
return is_callable($function);
}
public static function trim($str) {
return trim($str);
}
/* PLUGINS NAMESPACE */
/**
*
* @param $url
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $url
* @param $data
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $ajaxSettings
* @param $callback
* @param $param1
* @param $param2
* @param $param3
* @return phpQueryObject
*/
public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
if (self::plugin('WebBrowser')) {
$params = func_get_args();
return self::callbackRun(array(self::$plugins, 'browser'), $params);
} else {
self::debug('WebBrowser plugin not available...');
}
}
/**
*
* @param $code
* @return string
*/
public static function php($code) {
return self::code('php', $code);
}
/**
*
* @param $type
* @param $code
* @return string
*/
public static function code($type, $code) {
return "<$type><!-- ".trim($code)." --></$type>";
}
public static function __callStatic($method, $params) {
return call_user_func_array(
array(phpQuery::$plugins, $method),
$params
);
}
protected static function dataSetupNode($node, $documentID) {
// search are return if alredy exists
foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
if ($node->isSameNode($dataNode))
return $dataNode;
}
// if doesn't, add it
phpQuery::$documents[$documentID]->dataNodes[] = $node;
return $node;
}
protected static function dataRemoveNode($node, $documentID) {
// search are return if alredy exists
foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
if ($node->isSameNode($dataNode)) {
unset(self::$documents[$documentID]->dataNodes[$k]);
unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
}
}
}
public static function data($node, $name, $data, $documentID = null) {
if (! $documentID)
// TODO check if this works
$documentID = self::getDocumentID($node);
$document = phpQuery::$documents[$documentID];
$node = self::dataSetupNode($node, $documentID);
if (! isset($node->dataID))
$node->dataID = ++phpQuery::$documents[$documentID]->uuid;
$id = $node->dataID;
if (! isset($document->data[$id]))
$document->data[$id] = array();
if (! is_null($data))
$document->data[$id][$name] = $data;
if ($name) {
if (isset($document->data[$id][$name]))
return $document->data[$id][$name];
} else
return $id;
}
public static function removeData($node, $name, $documentID) {
if (! $documentID)
// TODO check if this works
$documentID = self::getDocumentID($node);
$document = phpQuery::$documents[$documentID];
$node = self::dataSetupNode($node, $documentID);
$id = $node->dataID;
if ($name) {
if (isset($document->data[$id][$name]))
unset($document->data[$id][$name]);
$name = null;
foreach($document->data[$id] as $name)
break;
if (! $name)
self::removeData($node, $name, $documentID);
} else {
self::dataRemoveNode($node, $documentID);
}
}
}
/**
* Plugins static namespace class.
*
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
* @todo move plugin methods here (as statics)
*/
class phpQueryPlugins {
public function __call($method, $args) {
if (isset(phpQuery::$extendStaticMethods[$method])) {
$return = call_user_func_array(
phpQuery::$extendStaticMethods[$method],
$args
);
} else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
$class = phpQuery::$pluginsStaticMethods[$method];
$realClass = "phpQueryPlugin_$class";
$return = call_user_func_array(
array($realClass, $method),
$args
);
return isset($return)
? $return
: $this;
} else
throw new Exception("Method '{$method}' doesnt exist");
}
}
/**
* Shortcut to phpQuery::pq($arg1, $context)
* Chainable.
*
* @see phpQuery::pq()
* @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
* @package phpQuery
*/
function pq($arg1, $context = null) {
$args = func_get_args();
return call_user_func_array(
array('phpQuery', 'pq'),
$args
);
}
// add plugins dir and Zend framework to include path
set_include_path(
get_include_path()
.PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
.PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
);
// why ? no __call nor __get for statics in php...
// XXX __callStatic will be available in PHP 5.3
phpQuery::$plugins = new phpQueryPlugins();
// include bootstrap file (personal library config)
if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
require_once dirname(__FILE__).'/phpQuery/bootstrap.php';