├── post.html
├── LICENSE
├── README.md
├── index.php
└── phpQuery-onefile.php
/post.html:
--------------------------------------------------------------------------------
1 |
2 | post.html - quickie just to POST stuff instead of GET.
3 |
4 |
5 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2011 Nicolas Noben
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | json-anything
2 | ----------------
3 |
4 | PHP proxy that converts webpages to json objects, filtered by jquery-like selectors (Built on top of PHPQuery).
5 |
6 | Takes parameters from GET or POST.
7 |
8 | Think of it as a **'shotgun api'**. Because many sites still don't provide apis.
9 |
10 | MIT LICENSE.
11 |
12 | ##Parameters:
13 |
14 | ###debug:
15 |
16 | **This is a debug flag.**
17 |
18 | If set, it will var_dump the results for improved readibility. If not, a json-encoded object is returned.
19 |
20 | ###url:
21 |
22 | **The url to go scrape and make JSON from.**
23 |
24 | For complex urls already using GET (ie. some search engines) you can POST url instead.
25 |
26 | **EXAMPLE**
27 |
28 | ?url=http://www.bom.gov.au/
29 |
30 |
31 | ###sel:
32 |
33 | **Comma separated list of selectors, in a jQuery fashion**
34 |
35 | PHP can't parse # from a url as it's never sent to the server ('url fragments').
36 |
37 | Because of this, **use % instead of #**.
38 |
39 | You can, but are not forced to, use two underscores (__) for a space (so it doesn't look like %20)
40 |
41 | Get attributes by using a '|'
42 |
43 | By default it will return 'text()'
44 |
45 | a ---> will return all text of the A tags
46 | a|html ---> will return all html contents of the A tags
47 | a|href ---> will return all href attribute of the A tags
48 | img|src ---> will return all src attribute of the img tags
49 |
50 | **EXAMPLES**
51 |
52 | span.hey ---> will return all spans with class hey
53 | span%hey ---> will return all spans with id hey (I know)
54 | div%pad table:first a ---> div#pad table:first a
55 |
56 | Complex Example using __ instead of space:
57 |
58 | Within #results, will return the 4th column, 2nd span, b's html
59 |
60 | %results__td:nth-child(4)__span:nth-child(2)__b|html
61 |
62 | ##Real world examples
63 |
64 | ###Latest currencies $ from Reuters
65 |
66 | index.php?url=http://uk.reuters.com/business/currencies&sel=%currPairs__td:first-child__a,%currPairs__td:first-child__a|href,%currPairs__td:nth- child(2)
67 |
68 | {
69 | "url":"http://uk.reuters.com/business/currencies",
70 | "sel":"#currPairs td:first-child a,#currPairs td:first-child a|href,#currPairs td:nth-child(2)",
71 | "results":[
72 | [
73 | "GBP/USD",
74 | "/business/currencies/quote?srcAmt=1&srcCurr=GBP&destAmt=&destCurr=USD",
75 | "1.6299"
76 | ],
77 | [
78 | "GBP/EUR",
79 | "/business/currencies/quote?srcAmt=1&srcCurr=GBP&destAmt=&destCurr=EUR",
80 | "1.1348"
81 | ],
82 |
83 | ...
84 | ]
85 | }
86 |
87 | ###Hacker News
88 |
89 | There is an unofficial api but anyway, this works too.
90 |
91 | http://localhost/json-anything/?url=http://news.ycombinator.com/&sel=.title__a:first-child,.title__a:first-child|href
92 |
93 | {
94 | "url":"http://news.ycombinator.com/",
95 | "sel":".title a:first-child,.title a:first-child|href",
96 | "results":[
97 | [
98 | "Octopress - A blogging framework for hackers",
99 | "http://octopress.org/"
100 | ],
101 | [
102 | "NoSQL is What?",
103 | "http://blog.zawodny.com/2011/07/23/nosql-is-what/"
104 | ],
105 | [
106 | "Radio Shack to start stocking Arduino, Other Goodies",
107 | "http://blog.radioshack.com/2011/07/21/top-ten-diy-suggestions-from-you/"
108 | ],
109 |
110 | ...
111 | ]
112 | }
113 |
114 | ###Australia Bureau of Meteorology (bom)
115 |
116 | The bom is known for not having decent a api.
117 |
118 | Take a look at its home page http://www.bom.gov.au/ ... We're going to grab the Forecast as presented for the major cities.
119 |
120 | http://localhost/json-anything/?url=http://www.bom.gov.au/&sel=%pad__table:first__a,%pad__table:first__.max,%pad__table:first__td:last-child
121 |
122 | url=http://www.bom.gov.au/
123 | %pad__table:first__a, (grab the #pad then the first table's A tags text)
124 | %pad__table:first__.max, (the maximas, has class .max)
125 | %pad__table:first__td:last-child (grab the last TD of the first table)
126 |
127 | {
128 | "url":"http://www.bom.gov.au/",
129 | "sel":"#pad table:first a,#pad table:first .max,#pad table:first td:last-child",
130 | "results":[
131 | [
132 | "Sydney",
133 | "17\u00b0",
134 | "Possible shower clearing."
135 | ],
136 | [
137 | "Melbourne",
138 | "14\u00b0",
139 | "Shower or two."
140 | ],
141 | [
142 | "Brisbane",
143 | "23\u00b0",
144 | "Fine."
145 | ],
146 |
147 | ...
148 | ]
149 | }
150 |
151 | (\u00b0 is the unicode for the DEGREE sign)
152 |
153 | ##Feel free to improve
154 |
155 | Not much of this is perfect, it comes from flaws in its quirky design, also the PHP could be improved.
156 |
157 | If you have some PHP experience in your legs and want to improve the code and/or add features, I'd gladly take changes (fork away!).
158 |
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | url = replaceThisByThatInThat('\\','',$url);
42 | $output->sel = $sel;
43 | $output->results = $ordered;
44 |
45 | $json_encoded_string = json_encode($output);
46 | $json_encoded_string = replaceThisByThatInThat('\\/', '/', $json_encoded_string);
47 |
48 |
49 | if($debug)
50 | var_dump($output);
51 | else
52 | echo($json_encoded_string);
53 |
54 |
55 |
56 | function parseParam($param)
57 | {
58 | if(!$_REQUEST[$param])
59 | die('MISSING parameter: ' . $param);
60 |
61 | return filterNastyStuff($_REQUEST[$param]);
62 | }
63 |
64 | function getIndividiualSelectors($commaDelimited)
65 | {
66 | return explode(',', $commaDelimited);
67 | }
68 |
69 | function separateSelectorsAndAttributes($array_selectors)
70 | {
71 | for ($i=0; $i < count($array_selectors); $i++)
72 | {
73 | $array_selectors[$i] = explode('|', $array_selectors[$i]);
74 |
75 | if(count($array_selectors[$i]) < 2)
76 | $array_selectors[$i][1] = null;
77 | }
78 |
79 | return $array_selectors;
80 | }
81 |
82 | // WARNING: not great.
83 | // if - for example - we're getting titles and links, and one of the results does not have a link, this will skip a beat!!
84 | // in other words, the final JSON results might skip a beat if one of the selectors, parsing the page, is not found
85 | // results can get out of whack quickly.
86 | function phpQueryFilterAllSelectors($selectors)
87 | {
88 | $all = array();
89 |
90 | foreach($selectors as $i => $oneSelector)
91 | {
92 | $all[$i] = (filterthis($oneSelector[0], $oneSelector[1]) );
93 | }
94 |
95 | return $all;
96 | }
97 |
98 | // does a phpQuery applying the selector, with conditions on the attribute
99 | function filterThis($selector, $attribute = null)
100 | {
101 | $arr = array();
102 |
103 | foreach(pq($selector) as $item)
104 | {
105 | if($attribute == null) // if no attribute, return text by default
106 | {
107 | array_push($arr, rtrim(ltrim(pq($item)->text())));
108 | }
109 | else if($attribute == 'html') // if html, return html content
110 | {
111 | array_push($arr, rtrim(ltrim(pq($item)->html())));
112 | }
113 | else // otherwise we've been give an attribute (src, href, alt, title...)
114 | array_push($arr, pq($item)->attr($attribute));
115 | }
116 |
117 | return $arr;
118 | }
119 |
120 | // takes arrays[0][X] and arrays[1][X], return arrays[X][0,1]
121 | // Warning: FRAGILE! phpQueryFilterAllSelectors probably needs to be smarter.
122 | // used when we have more than one selector, the results arrays are given one selector at a time
123 | // we use it to merge so that titles and links (for example) go together.
124 | // example:
125 | // takes [ [title, title, title], [link, link, link], [name, name, name] ]
126 | // returns [ [title, link, name], [title, link, name], [title, link, name] ]
127 | // Warning: Dumb code! if one of the link is missing, it will skip a beat!
128 | function dumbMergeArrays($arrays)
129 | {
130 | $result = array();
131 | $temp = array();
132 |
133 | foreach($arrays as $onearr)
134 | {
135 | $temp = array_merge($temp, $onearr);
136 | }
137 |
138 | $nominal_length = count($arrays[0]);
139 | $total_arrays = count($arrays);
140 |
141 | for ($i=0; $i < $nominal_length; $i++)
142 | {
143 | for ($j=0; $j < $total_arrays; $j++)
144 | {
145 | try {
146 | $result[$i][$j] = $temp[ $i + ($nominal_length * $j) ];
147 | } catch(Exception $e) {}
148 | }
149 | }
150 |
151 | return $result;
152 | }
153 |
154 | // simple shorthand for the obfuscated str_replace
155 | function replaceThisByThatInThat($this, $byThat, $inThat)
156 | {
157 | return str_replace($this, $byThat, $inThat);
158 | }
159 |
160 | // killing a bird with a shotgun - improve me if you can
161 | function filterNastyStuff($stuff) {
162 | return filter_var
163 | (
164 | htmlspecialchars
165 | (
166 | pg_escape_string
167 | (
168 | strip_tags($stuff)
169 | )
170 | ), FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH
171 | );
172 | }
173 |
174 | ?>
175 |
176 |
177 |
--------------------------------------------------------------------------------
/phpQuery-onefile.php:
--------------------------------------------------------------------------------
1 |
11 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
12 | * @package phpQuery
13 | */
14 |
15 | // class names for instanceof
16 | // TODO move them as class constants into phpQuery
17 | define('DOMDOCUMENT', 'DOMDocument');
18 | define('DOMELEMENT', 'DOMElement');
19 | define('DOMNODELIST', 'DOMNodeList');
20 | define('DOMNODE', 'DOMNode');
21 |
22 | /**
23 | * DOMEvent class.
24 | *
25 | * Based on
26 | * @link http://developer.mozilla.org/En/DOM:event
27 | * @author Tobiasz Cudnik
28 | * @package phpQuery
29 | * @todo implement ArrayAccess ?
30 | */
31 | class DOMEvent {
32 | /**
33 | * Returns a boolean indicating whether the event bubbles up through the DOM or not.
34 | *
35 | * @var unknown_type
36 | */
37 | public $bubbles = true;
38 | /**
39 | * Returns a boolean indicating whether the event is cancelable.
40 | *
41 | * @var unknown_type
42 | */
43 | public $cancelable = true;
44 | /**
45 | * Returns a reference to the currently registered target for the event.
46 | *
47 | * @var unknown_type
48 | */
49 | public $currentTarget;
50 | /**
51 | * Returns detail about the event, depending on the type of event.
52 | *
53 | * @var unknown_type
54 | * @link http://developer.mozilla.org/en/DOM/event.detail
55 | */
56 | public $detail; // ???
57 | /**
58 | * Used to indicate which phase of the event flow is currently being evaluated.
59 | *
60 | * NOT IMPLEMENTED
61 | *
62 | * @var unknown_type
63 | * @link http://developer.mozilla.org/en/DOM/event.eventPhase
64 | */
65 | public $eventPhase; // ???
66 | /**
67 | * The explicit original target of the event (Mozilla-specific).
68 | *
69 | * NOT IMPLEMENTED
70 | *
71 | * @var unknown_type
72 | */
73 | public $explicitOriginalTarget; // moz only
74 | /**
75 | * The original target of the event, before any retargetings (Mozilla-specific).
76 | *
77 | * NOT IMPLEMENTED
78 | *
79 | * @var unknown_type
80 | */
81 | public $originalTarget; // moz only
82 | /**
83 | * Identifies a secondary target for the event.
84 | *
85 | * @var unknown_type
86 | */
87 | public $relatedTarget;
88 | /**
89 | * Returns a reference to the target to which the event was originally dispatched.
90 | *
91 | * @var unknown_type
92 | */
93 | public $target;
94 | /**
95 | * Returns the time that the event was created.
96 | *
97 | * @var unknown_type
98 | */
99 | public $timeStamp;
100 | /**
101 | * Returns the name of the event (case-insensitive).
102 | */
103 | public $type;
104 | public $runDefault = true;
105 | public $data = null;
106 | public function __construct($data) {
107 | foreach($data as $k => $v) {
108 | $this->$k = $v;
109 | }
110 | if (! $this->timeStamp)
111 | $this->timeStamp = time();
112 | }
113 | /**
114 | * Cancels the event (if it is cancelable).
115 | *
116 | */
117 | public function preventDefault() {
118 | $this->runDefault = false;
119 | }
120 | /**
121 | * Stops the propagation of events further along in the DOM.
122 | *
123 | */
124 | public function stopPropagation() {
125 | $this->bubbles = false;
126 | }
127 | }
128 |
129 |
130 | /**
131 | * DOMDocumentWrapper class simplifies work with DOMDocument.
132 | *
133 | * Know bug:
134 | * - in XHTML fragments,
changes to
135 | *
136 | * @todo check XML catalogs compatibility
137 | * @author Tobiasz Cudnik
138 | * @package phpQuery
139 | */
140 | class DOMDocumentWrapper {
141 | /**
142 | * @var DOMDocument
143 | */
144 | public $document;
145 | public $id;
146 | /**
147 | * @todo Rewrite as method and quess if null.
148 | * @var unknown_type
149 | */
150 | public $contentType = '';
151 | public $xpath;
152 | public $uuid = 0;
153 | public $data = array();
154 | public $dataNodes = array();
155 | public $events = array();
156 | public $eventsNodes = array();
157 | public $eventsGlobal = array();
158 | /**
159 | * @TODO iframes support http://code.google.com/p/phpquery/issues/detail?id=28
160 | * @var unknown_type
161 | */
162 | public $frames = array();
163 | /**
164 | * Document root, by default equals to document itself.
165 | * Used by documentFragments.
166 | *
167 | * @var DOMNode
168 | */
169 | public $root;
170 | public $isDocumentFragment;
171 | public $isXML = false;
172 | public $isXHTML = false;
173 | public $isHTML = false;
174 | public $charset;
175 | public function __construct($markup = null, $contentType = null, $newDocumentID = null) {
176 | if (isset($markup))
177 | $this->load($markup, $contentType, $newDocumentID);
178 | $this->id = $newDocumentID
179 | ? $newDocumentID
180 | : md5(microtime());
181 | }
182 | public function load($markup, $contentType = null, $newDocumentID = null) {
183 | // phpQuery::$documents[$id] = $this;
184 | $this->contentType = strtolower($contentType);
185 | if ($markup instanceof DOMDOCUMENT) {
186 | $this->document = $markup;
187 | $this->root = $this->document;
188 | $this->charset = $this->document->encoding;
189 | // TODO isDocumentFragment
190 | } else {
191 | $loaded = $this->loadMarkup($markup);
192 | }
193 | if ($loaded) {
194 | // $this->document->formatOutput = true;
195 | $this->document->preserveWhiteSpace = true;
196 | $this->xpath = new DOMXPath($this->document);
197 | $this->afterMarkupLoad();
198 | return true;
199 | // remember last loaded document
200 | // return phpQuery::selectDocument($id);
201 | }
202 | return false;
203 | }
204 | protected function afterMarkupLoad() {
205 | if ($this->isXHTML) {
206 | $this->xpath->registerNamespace("html", "http://www.w3.org/1999/xhtml");
207 | }
208 | }
209 | protected function loadMarkup($markup) {
210 | $loaded = false;
211 | if ($this->contentType) {
212 | self::debug("Load markup for content type {$this->contentType}");
213 | // content determined by contentType
214 | list($contentType, $charset) = $this->contentTypeToArray($this->contentType);
215 | switch($contentType) {
216 | case 'text/html':
217 | phpQuery::debug("Loading HTML, content type '{$this->contentType}'");
218 | $loaded = $this->loadMarkupHTML($markup, $charset);
219 | break;
220 | case 'text/xml':
221 | case 'application/xhtml+xml':
222 | phpQuery::debug("Loading XML, content type '{$this->contentType}'");
223 | $loaded = $this->loadMarkupXML($markup, $charset);
224 | break;
225 | default:
226 | // for feeds or anything that sometimes doesn't use text/xml
227 | if (strpos('xml', $this->contentType) !== false) {
228 | phpQuery::debug("Loading XML, content type '{$this->contentType}'");
229 | $loaded = $this->loadMarkupXML($markup, $charset);
230 | } else
231 | phpQuery::debug("Could not determine document type from content type '{$this->contentType}'");
232 | }
233 | } else {
234 | // content type autodetection
235 | if ($this->isXML($markup)) {
236 | phpQuery::debug("Loading XML, isXML() == true");
237 | $loaded = $this->loadMarkupXML($markup);
238 | if (! $loaded && $this->isXHTML) {
239 | phpQuery::debug('Loading as XML failed, trying to load as HTML, isXHTML == true');
240 | $loaded = $this->loadMarkupHTML($markup);
241 | }
242 | } else {
243 | phpQuery::debug("Loading HTML, isXML() == false");
244 | $loaded = $this->loadMarkupHTML($markup);
245 | }
246 | }
247 | return $loaded;
248 | }
249 | protected function loadMarkupReset() {
250 | $this->isXML = $this->isXHTML = $this->isHTML = false;
251 | }
252 | protected function documentCreate($charset, $version = '1.0') {
253 | if (! $version)
254 | $version = '1.0';
255 | $this->document = new DOMDocument($version, $charset);
256 | $this->charset = $this->document->encoding;
257 | // $this->document->encoding = $charset;
258 | $this->document->formatOutput = true;
259 | $this->document->preserveWhiteSpace = true;
260 | }
261 | protected function loadMarkupHTML($markup, $requestedCharset = null) {
262 | if (phpQuery::$debug)
263 | phpQuery::debug('Full markup load (HTML): '.substr($markup, 0, 250));
264 | $this->loadMarkupReset();
265 | $this->isHTML = true;
266 | if (!isset($this->isDocumentFragment))
267 | $this->isDocumentFragment = self::isDocumentFragmentHTML($markup);
268 | $charset = null;
269 | $documentCharset = $this->charsetFromHTML($markup);
270 | $addDocumentCharset = false;
271 | if ($documentCharset) {
272 | $charset = $documentCharset;
273 | $markup = $this->charsetFixHTML($markup);
274 | } else if ($requestedCharset) {
275 | $charset = $requestedCharset;
276 | }
277 | if (! $charset)
278 | $charset = phpQuery::$defaultCharset;
279 | // HTTP 1.1 says that the default charset is ISO-8859-1
280 | // @see http://www.w3.org/International/O-HTTP-charset
281 | if (! $documentCharset) {
282 | $documentCharset = 'ISO-8859-1';
283 | $addDocumentCharset = true;
284 | }
285 | // Should be careful here, still need 'magic encoding detection' since lots of pages have other 'default encoding'
286 | // Worse, some pages can have mixed encodings... we'll try not to worry about that
287 | $requestedCharset = strtoupper($requestedCharset);
288 | $documentCharset = strtoupper($documentCharset);
289 | phpQuery::debug("DOC: $documentCharset REQ: $requestedCharset");
290 | if ($requestedCharset && $documentCharset && $requestedCharset !== $documentCharset) {
291 | phpQuery::debug("CHARSET CONVERT");
292 | // Document Encoding Conversion
293 | // http://code.google.com/p/phpquery/issues/detail?id=86
294 | if (function_exists('mb_detect_encoding')) {
295 | $possibleCharsets = array($documentCharset, $requestedCharset, 'AUTO');
296 | $docEncoding = mb_detect_encoding($markup, implode(', ', $possibleCharsets));
297 | if (! $docEncoding)
298 | $docEncoding = $documentCharset; // ok trust the document
299 | phpQuery::debug("DETECTED '$docEncoding'");
300 | // Detected does not match what document says...
301 | if ($docEncoding !== $documentCharset) {
302 | // Tricky..
303 | }
304 | if ($docEncoding !== $requestedCharset) {
305 | phpQuery::debug("CONVERT $docEncoding => $requestedCharset");
306 | $markup = mb_convert_encoding($markup, $requestedCharset, $docEncoding);
307 | $markup = $this->charsetAppendToHTML($markup, $requestedCharset);
308 | $charset = $requestedCharset;
309 | }
310 | } else {
311 | phpQuery::debug("TODO: charset conversion without mbstring...");
312 | }
313 | }
314 | $return = false;
315 | if ($this->isDocumentFragment) {
316 | phpQuery::debug("Full markup load (HTML), DocumentFragment detected, using charset '$charset'");
317 | $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
318 | } else {
319 | if ($addDocumentCharset) {
320 | phpQuery::debug("Full markup load (HTML), appending charset: '$charset'");
321 | $markup = $this->charsetAppendToHTML($markup, $charset);
322 | }
323 | phpQuery::debug("Full markup load (HTML), documentCreate('$charset')");
324 | $this->documentCreate($charset);
325 | $return = phpQuery::$debug === 2
326 | ? $this->document->loadHTML($markup)
327 | : @$this->document->loadHTML($markup);
328 | if ($return)
329 | $this->root = $this->document;
330 | }
331 | if ($return && ! $this->contentType)
332 | $this->contentType = 'text/html';
333 | return $return;
334 | }
335 | protected function loadMarkupXML($markup, $requestedCharset = null) {
336 | if (phpQuery::$debug)
337 | phpQuery::debug('Full markup load (XML): '.substr($markup, 0, 250));
338 | $this->loadMarkupReset();
339 | $this->isXML = true;
340 | // check agains XHTML in contentType or markup
341 | $isContentTypeXHTML = $this->isXHTML();
342 | $isMarkupXHTML = $this->isXHTML($markup);
343 | if ($isContentTypeXHTML || $isMarkupXHTML) {
344 | self::debug('Full markup load (XML), XHTML detected');
345 | $this->isXHTML = true;
346 | }
347 | // determine document fragment
348 | if (! isset($this->isDocumentFragment))
349 | $this->isDocumentFragment = $this->isXHTML
350 | ? self::isDocumentFragmentXHTML($markup)
351 | : self::isDocumentFragmentXML($markup);
352 | // this charset will be used
353 | $charset = null;
354 | // charset from XML declaration @var string
355 | $documentCharset = $this->charsetFromXML($markup);
356 | if (! $documentCharset) {
357 | if ($this->isXHTML) {
358 | // this is XHTML, try to get charset from content-type meta header
359 | $documentCharset = $this->charsetFromHTML($markup);
360 | if ($documentCharset) {
361 | phpQuery::debug("Full markup load (XML), appending XHTML charset '$documentCharset'");
362 | $this->charsetAppendToXML($markup, $documentCharset);
363 | $charset = $documentCharset;
364 | }
365 | }
366 | if (! $documentCharset) {
367 | // if still no document charset...
368 | $charset = $requestedCharset;
369 | }
370 | } else if ($requestedCharset) {
371 | $charset = $requestedCharset;
372 | }
373 | if (! $charset) {
374 | $charset = phpQuery::$defaultCharset;
375 | }
376 | if ($requestedCharset && $documentCharset && $requestedCharset != $documentCharset) {
377 | // TODO place for charset conversion
378 | // $charset = $requestedCharset;
379 | }
380 | $return = false;
381 | if ($this->isDocumentFragment) {
382 | phpQuery::debug("Full markup load (XML), DocumentFragment detected, using charset '$charset'");
383 | $return = $this->documentFragmentLoadMarkup($this, $charset, $markup);
384 | } else {
385 | // FIXME ???
386 | if ($isContentTypeXHTML && ! $isMarkupXHTML)
387 | if (! $documentCharset) {
388 | phpQuery::debug("Full markup load (XML), appending charset '$charset'");
389 | $markup = $this->charsetAppendToXML($markup, $charset);
390 | }
391 | // see http://pl2.php.net/manual/en/book.dom.php#78929
392 | // LIBXML_DTDLOAD (>= PHP 5.1)
393 | // does XML ctalogues works with LIBXML_NONET
394 | // $this->document->resolveExternals = true;
395 | // TODO test LIBXML_COMPACT for performance improvement
396 | // create document
397 | $this->documentCreate($charset);
398 | if (phpversion() < 5.1) {
399 | $this->document->resolveExternals = true;
400 | $return = phpQuery::$debug === 2
401 | ? $this->document->loadXML($markup)
402 | : @$this->document->loadXML($markup);
403 | } else {
404 | /** @link http://pl2.php.net/manual/en/libxml.constants.php */
405 | $libxmlStatic = phpQuery::$debug === 2
406 | ? LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET
407 | : LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOERROR;
408 | $return = $this->document->loadXML($markup, $libxmlStatic);
409 | // if (! $return)
410 | // $return = $this->document->loadHTML($markup);
411 | }
412 | if ($return)
413 | $this->root = $this->document;
414 | }
415 | if ($return) {
416 | if (! $this->contentType) {
417 | if ($this->isXHTML)
418 | $this->contentType = 'application/xhtml+xml';
419 | else
420 | $this->contentType = 'text/xml';
421 | }
422 | return $return;
423 | } else {
424 | throw new Exception("Error loading XML markup");
425 | }
426 | }
427 | protected function isXHTML($markup = null) {
428 | if (! isset($markup)) {
429 | return strpos($this->contentType, 'xhtml') !== false;
430 | }
431 | // XXX ok ?
432 | return strpos($markup, "doctype) && is_object($dom->doctype)
435 | // ? $dom->doctype->publicId
436 | // : self::$defaultDoctype;
437 | }
438 | protected function isXML($markup) {
439 | // return strpos($markup, ']+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
463 | $markup, $matches
464 | );
465 | if (! isset($matches[0]))
466 | return array(null, null);
467 | // get attr 'content'
468 | preg_match('@content\\s*=\\s*(["|\'])(.+?)\\1@', $matches[0], $matches);
469 | if (! isset($matches[0]))
470 | return array(null, null);
471 | return $this->contentTypeToArray($matches[2]);
472 | }
473 | protected function charsetFromHTML($markup) {
474 | $contentType = $this->contentTypeFromHTML($markup);
475 | return $contentType[1];
476 | }
477 | protected function charsetFromXML($markup) {
478 | $matches;
479 | // find declaration
480 | preg_match('@<'.'?xml[^>]+encoding\\s*=\\s*(["|\'])(.*?)\\1@i',
481 | $markup, $matches
482 | );
483 | return isset($matches[2])
484 | ? strtolower($matches[2])
485 | : null;
486 | }
487 | /**
488 | * Repositions meta[type=charset] at the start of head. Bypasses DOMDocument bug.
489 | *
490 | * @link http://code.google.com/p/phpquery/issues/detail?id=80
491 | * @param $html
492 | */
493 | protected function charsetFixHTML($markup) {
494 | $matches = array();
495 | // find meta tag
496 | preg_match('@\s*]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i',
497 | $markup, $matches, PREG_OFFSET_CAPTURE
498 | );
499 | if (! isset($matches[0]))
500 | return;
501 | $metaContentType = $matches[0][0];
502 | $markup = substr($markup, 0, $matches[0][1])
503 | .substr($markup, $matches[0][1]+strlen($metaContentType));
504 | $headStart = stripos($markup, '');
505 | $markup = substr($markup, 0, $headStart+6).$metaContentType
506 | .substr($markup, $headStart+6);
507 | return $markup;
508 | }
509 | protected function charsetAppendToHTML($html, $charset, $xhtml = false) {
510 | // remove existing meta[type=content-type]
511 | $html = preg_replace('@\s*]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
512 | $meta = '';
516 | if (strpos($html, ')@s',
522 | "{$meta}",
523 | $html
524 | );
525 | }
526 | } else {
527 | return preg_replace(
528 | '@)@s',
529 | ''.$meta,
530 | $html
531 | );
532 | }
533 | }
534 | protected function charsetAppendToXML($markup, $charset) {
535 | $declaration = '<'.'?xml version="1.0" encoding="'.$charset.'"?'.'>';
536 | return $declaration.$markup;
537 | }
538 | public static function isDocumentFragmentHTML($markup) {
539 | return stripos($markup, 'documentFragmentCreate($node, $sourceCharset);
567 | // if ($fake === false)
568 | // throw new Exception("Error loading documentFragment markup");
569 | // else
570 | // $return = array_merge($return,
571 | // $this->import($fake->root->childNodes)
572 | // );
573 | // } else {
574 | // $return[] = $this->document->importNode($node, true);
575 | // }
576 | // }
577 | // return $return;
578 | // } else {
579 | // // string markup
580 | // $fake = $this->documentFragmentCreate($source, $sourceCharset);
581 | // if ($fake === false)
582 | // throw new Exception("Error loading documentFragment markup");
583 | // else
584 | // return $this->import($fake->root->childNodes);
585 | // }
586 | if (is_array($source) || $source instanceof DOMNODELIST) {
587 | // dom nodes
588 | self::debug('Importing nodes to document');
589 | foreach($source as $node)
590 | $return[] = $this->document->importNode($node, true);
591 | } else {
592 | // string markup
593 | $fake = $this->documentFragmentCreate($source, $sourceCharset);
594 | if ($fake === false)
595 | throw new Exception("Error loading documentFragment markup");
596 | else
597 | return $this->import($fake->root->childNodes);
598 | }
599 | return $return;
600 | }
601 | /**
602 | * Creates new document fragment.
603 | *
604 | * @param $source
605 | * @return DOMDocumentWrapper
606 | */
607 | protected function documentFragmentCreate($source, $charset = null) {
608 | $fake = new DOMDocumentWrapper();
609 | $fake->contentType = $this->contentType;
610 | $fake->isXML = $this->isXML;
611 | $fake->isHTML = $this->isHTML;
612 | $fake->isXHTML = $this->isXHTML;
613 | $fake->root = $fake->document;
614 | if (! $charset)
615 | $charset = $this->charset;
616 | // $fake->documentCreate($this->charset);
617 | if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
618 | $source = array($source);
619 | if (is_array($source) || $source instanceof DOMNODELIST) {
620 | // dom nodes
621 | // load fake document
622 | if (! $this->documentFragmentLoadMarkup($fake, $charset))
623 | return false;
624 | $nodes = $fake->import($source);
625 | foreach($nodes as $node)
626 | $fake->root->appendChild($node);
627 | } else {
628 | // string markup
629 | $this->documentFragmentLoadMarkup($fake, $charset, $source);
630 | }
631 | return $fake;
632 | }
633 | /**
634 | *
635 | * @param $document DOMDocumentWrapper
636 | * @param $markup
637 | * @return $document
638 | */
639 | private function documentFragmentLoadMarkup($fragment, $charset, $markup = null) {
640 | // TODO error handling
641 | // TODO copy doctype
642 | // tempolary turn off
643 | $fragment->isDocumentFragment = false;
644 | if ($fragment->isXML) {
645 | if ($fragment->isXHTML) {
646 | // add FAKE element to set default namespace
647 | $fragment->loadMarkupXML(''
648 | .''
650 | .''.$markup.'');
651 | $fragment->root = $fragment->document->firstChild->nextSibling;
652 | } else {
653 | $fragment->loadMarkupXML(''.$markup.'');
654 | $fragment->root = $fragment->document->firstChild;
655 | }
656 | } else {
657 | $markup2 = phpQuery::$defaultDoctype.'';
659 | $noBody = strpos($markup, 'loadMarkupHTML($markup2);
667 | // TODO resolv body tag merging issue
668 | $fragment->root = $noBody
669 | ? $fragment->document->firstChild->nextSibling->firstChild->nextSibling
670 | : $fragment->document->firstChild->nextSibling->firstChild->nextSibling;
671 | }
672 | if (! $fragment->root)
673 | return false;
674 | $fragment->isDocumentFragment = true;
675 | return true;
676 | }
677 | protected function documentFragmentToMarkup($fragment) {
678 | phpQuery::debug('documentFragmentToMarkup');
679 | $tmp = $fragment->isDocumentFragment;
680 | $fragment->isDocumentFragment = false;
681 | $markup = $fragment->markup();
682 | if ($fragment->isXML) {
683 | $markup = substr($markup, 0, strrpos($markup, ''));
684 | if ($fragment->isXHTML) {
685 | $markup = substr($markup, strpos($markup, '')+6);
688 | }
689 | } else {
690 | $markup = substr($markup, strpos($markup, '')+6);
691 | $markup = substr($markup, 0, strrpos($markup, ''));
692 | }
693 | $fragment->isDocumentFragment = $tmp;
694 | if (phpQuery::$debug)
695 | phpQuery::debug('documentFragmentToMarkup: '.substr($markup, 0, 150));
696 | return $markup;
697 | }
698 | /**
699 | * Return document markup, starting with optional $nodes as root.
700 | *
701 | * @param $nodes DOMNode|DOMNodeList
702 | * @return string
703 | */
704 | public function markup($nodes = null, $innerMarkup = false) {
705 | if (isset($nodes) && count($nodes) == 1 && $nodes[0] instanceof DOMDOCUMENT)
706 | $nodes = null;
707 | if (isset($nodes)) {
708 | $markup = '';
709 | if (!is_array($nodes) && !($nodes instanceof DOMNODELIST) )
710 | $nodes = array($nodes);
711 | if ($this->isDocumentFragment && ! $innerMarkup)
712 | foreach($nodes as $i => $node)
713 | if ($node->isSameNode($this->root)) {
714 | // var_dump($node);
715 | $nodes = array_slice($nodes, 0, $i)
716 | + phpQuery::DOMNodeListToArray($node->childNodes)
717 | + array_slice($nodes, $i+1);
718 | }
719 | if ($this->isXML && ! $innerMarkup) {
720 | self::debug("Getting outerXML with charset '{$this->charset}'");
721 | // we need outerXML, so we can benefit from
722 | // $node param support in saveXML()
723 | foreach($nodes as $node)
724 | $markup .= $this->document->saveXML($node);
725 | } else {
726 | $loop = array();
727 | if ($innerMarkup)
728 | foreach($nodes as $node) {
729 | if ($node->childNodes)
730 | foreach($node->childNodes as $child)
731 | $loop[] = $child;
732 | else
733 | $loop[] = $node;
734 | }
735 | else
736 | $loop = $nodes;
737 | self::debug("Getting markup, moving selected nodes (".count($loop).") to new DocumentFragment");
738 | $fake = $this->documentFragmentCreate($loop);
739 | $markup = $this->documentFragmentToMarkup($fake);
740 | }
741 | if ($this->isXHTML) {
742 | self::debug("Fixing XHTML");
743 | $markup = self::markupFixXHTML($markup);
744 | }
745 | self::debug("Markup: ".substr($markup, 0, 250));
746 | return $markup;
747 | } else {
748 | if ($this->isDocumentFragment) {
749 | // documentFragment, html only...
750 | self::debug("Getting markup, DocumentFragment detected");
751 | // return $this->markup(
752 | //// $this->document->getElementsByTagName('body')->item(0)
753 | // $this->document->root, true
754 | // );
755 | $markup = $this->documentFragmentToMarkup($this);
756 | // no need for markupFixXHTML, as it's done thought markup($nodes) method
757 | return $markup;
758 | } else {
759 | self::debug("Getting markup (".($this->isXML?'XML':'HTML')."), final with charset '{$this->charset}'");
760 | $markup = $this->isXML
761 | ? $this->document->saveXML()
762 | : $this->document->saveHTML();
763 | if ($this->isXHTML) {
764 | self::debug("Fixing XHTML");
765 | $markup = self::markupFixXHTML($markup);
766 | }
767 | self::debug("Markup: ".substr($markup, 0, 250));
768 | return $markup;
769 | }
770 | }
771 | }
772 | protected static function markupFixXHTML($markup) {
773 | $markup = self::expandEmptyTag('script', $markup);
774 | $markup = self::expandEmptyTag('select', $markup);
775 | $markup = self::expandEmptyTag('textarea', $markup);
776 | return $markup;
777 | }
778 | public static function debug($text) {
779 | phpQuery::debug($text);
780 | }
781 | /**
782 | * expandEmptyTag
783 | *
784 | * @param $tag
785 | * @param $xml
786 | * @return unknown_type
787 | * @author mjaque at ilkebenson dot com
788 | * @link http://php.net/manual/en/domdocument.savehtml.php#81256
789 | */
790 | public static function expandEmptyTag($tag, $xml){
791 | $indice = 0;
792 | while ($indice< strlen($xml)){
793 | $pos = strpos($xml, "<$tag ", $indice);
794 | if ($pos){
795 | $posCierre = strpos($xml, ">", $pos);
796 | if ($xml[$posCierre-1] == "/"){
797 | $xml = substr_replace($xml, ">$tag>", $posCierre-1, 2);
798 | }
799 | $indice = $posCierre;
800 | }
801 | else break;
802 | }
803 | return $xml;
804 | }
805 | }
806 |
807 | /**
808 | * Event handling class.
809 | *
810 | * @author Tobiasz Cudnik
811 | * @package phpQuery
812 | * @static
813 | */
814 | abstract class phpQueryEvents {
815 | /**
816 | * Trigger a type of event on every matched element.
817 | *
818 | * @param DOMNode|phpQueryObject|string $document
819 | * @param unknown_type $type
820 | * @param unknown_type $data
821 | *
822 | * @TODO exclusive events (with !)
823 | * @TODO global events (test)
824 | * @TODO support more than event in $type (space-separated)
825 | */
826 | public static function trigger($document, $type, $data = array(), $node = null) {
827 | // trigger: function(type, data, elem, donative, extra) {
828 | $documentID = phpQuery::getDocumentID($document);
829 | $namespace = null;
830 | if (strpos($type, '.') !== false)
831 | list($name, $namespace) = explode('.', $type);
832 | else
833 | $name = $type;
834 | if (! $node) {
835 | if (self::issetGlobal($documentID, $type)) {
836 | $pq = phpQuery::getDocument($documentID);
837 | // TODO check add($pq->document)
838 | $pq->find('*')->add($pq->document)
839 | ->trigger($type, $data);
840 | }
841 | } else {
842 | if (isset($data[0]) && $data[0] instanceof DOMEvent) {
843 | $event = $data[0];
844 | $event->relatedTarget = $event->target;
845 | $event->target = $node;
846 | $data = array_slice($data, 1);
847 | } else {
848 | $event = new DOMEvent(array(
849 | 'type' => $type,
850 | 'target' => $node,
851 | 'timeStamp' => time(),
852 | ));
853 | }
854 | $i = 0;
855 | while($node) {
856 | // TODO whois
857 | phpQuery::debug("Triggering ".($i?"bubbled ":'')."event '{$type}' on "
858 | ."node \n");//.phpQueryObject::whois($node)."\n");
859 | $event->currentTarget = $node;
860 | $eventNode = self::getNode($documentID, $node);
861 | if (isset($eventNode->eventHandlers)) {
862 | foreach($eventNode->eventHandlers as $eventType => $handlers) {
863 | $eventNamespace = null;
864 | if (strpos($type, '.') !== false)
865 | list($eventName, $eventNamespace) = explode('.', $eventType);
866 | else
867 | $eventName = $eventType;
868 | if ($name != $eventName)
869 | continue;
870 | if ($namespace && $eventNamespace && $namespace != $eventNamespace)
871 | continue;
872 | foreach($handlers as $handler) {
873 | phpQuery::debug("Calling event handler\n");
874 | $event->data = $handler['data']
875 | ? $handler['data']
876 | : null;
877 | $params = array_merge(array($event), $data);
878 | $return = phpQuery::callbackRun($handler['callback'], $params);
879 | if ($return === false) {
880 | $event->bubbles = false;
881 | }
882 | }
883 | }
884 | }
885 | // to bubble or not to bubble...
886 | if (! $event->bubbles)
887 | break;
888 | $node = $node->parentNode;
889 | $i++;
890 | }
891 | }
892 | }
893 | /**
894 | * Binds a handler to one or more events (like click) for each matched element.
895 | * Can also bind custom events.
896 | *
897 | * @param DOMNode|phpQueryObject|string $document
898 | * @param unknown_type $type
899 | * @param unknown_type $data Optional
900 | * @param unknown_type $callback
901 | *
902 | * @TODO support '!' (exclusive) events
903 | * @TODO support more than event in $type (space-separated)
904 | * @TODO support binding to global events
905 | */
906 | public static function add($document, $node, $type, $data, $callback = null) {
907 | phpQuery::debug("Binding '$type' event");
908 | $documentID = phpQuery::getDocumentID($document);
909 | // if (is_null($callback) && is_callable($data)) {
910 | // $callback = $data;
911 | // $data = null;
912 | // }
913 | $eventNode = self::getNode($documentID, $node);
914 | if (! $eventNode)
915 | $eventNode = self::setNode($documentID, $node);
916 | if (!isset($eventNode->eventHandlers[$type]))
917 | $eventNode->eventHandlers[$type] = array();
918 | $eventNode->eventHandlers[$type][] = array(
919 | 'callback' => $callback,
920 | 'data' => $data,
921 | );
922 | }
923 | /**
924 | * Enter description here...
925 | *
926 | * @param DOMNode|phpQueryObject|string $document
927 | * @param unknown_type $type
928 | * @param unknown_type $callback
929 | *
930 | * @TODO namespace events
931 | * @TODO support more than event in $type (space-separated)
932 | */
933 | public static function remove($document, $node, $type = null, $callback = null) {
934 | $documentID = phpQuery::getDocumentID($document);
935 | $eventNode = self::getNode($documentID, $node);
936 | if (is_object($eventNode) && isset($eventNode->eventHandlers[$type])) {
937 | if ($callback) {
938 | foreach($eventNode->eventHandlers[$type] as $k => $handler)
939 | if ($handler['callback'] == $callback)
940 | unset($eventNode->eventHandlers[$type][$k]);
941 | } else {
942 | unset($eventNode->eventHandlers[$type]);
943 | }
944 | }
945 | }
946 | protected static function getNode($documentID, $node) {
947 | foreach(phpQuery::$documents[$documentID]->eventsNodes as $eventNode) {
948 | if ($node->isSameNode($eventNode))
949 | return $eventNode;
950 | }
951 | }
952 | protected static function setNode($documentID, $node) {
953 | phpQuery::$documents[$documentID]->eventsNodes[] = $node;
954 | return phpQuery::$documents[$documentID]->eventsNodes[
955 | count(phpQuery::$documents[$documentID]->eventsNodes)-1
956 | ];
957 | }
958 | protected static function issetGlobal($documentID, $type) {
959 | return isset(phpQuery::$documents[$documentID])
960 | ? in_array($type, phpQuery::$documents[$documentID]->eventsGlobal)
961 | : false;
962 | }
963 | }
964 |
965 |
966 | interface ICallbackNamed {
967 | function hasName();
968 | function getName();
969 | }
970 | /**
971 | * Callback class introduces currying-like pattern.
972 | *
973 | * Example:
974 | * function foo($param1, $param2, $param3) {
975 | * var_dump($param1, $param2, $param3);
976 | * }
977 | * $fooCurried = new Callback('foo',
978 | * 'param1 is now statically set',
979 | * new CallbackParam, new CallbackParam
980 | * );
981 | * phpQuery::callbackRun($fooCurried,
982 | * array('param2 value', 'param3 value'
983 | * );
984 | *
985 | * Callback class is supported in all phpQuery methods which accepts callbacks.
986 | *
987 | * @link http://code.google.com/p/phpquery/wiki/Callbacks#Param_Structures
988 | * @author Tobiasz Cudnik
989 | *
990 | * @TODO??? return fake forwarding function created via create_function
991 | * @TODO honor paramStructure
992 | */
993 | class Callback
994 | implements ICallbackNamed {
995 | public $callback = null;
996 | public $params = null;
997 | protected $name;
998 | public function __construct($callback, $param1 = null, $param2 = null,
999 | $param3 = null) {
1000 | $params = func_get_args();
1001 | $params = array_slice($params, 1);
1002 | if ($callback instanceof Callback) {
1003 | // TODO implement recurention
1004 | } else {
1005 | $this->callback = $callback;
1006 | $this->params = $params;
1007 | }
1008 | }
1009 | public function getName() {
1010 | return 'Callback: '.$this->name;
1011 | }
1012 | public function hasName() {
1013 | return isset($this->name) && $this->name;
1014 | }
1015 | public function setName($name) {
1016 | $this->name = $name;
1017 | return $this;
1018 | }
1019 | // TODO test me
1020 | // public function addParams() {
1021 | // $params = func_get_args();
1022 | // return new Callback($this->callback, $this->params+$params);
1023 | // }
1024 | }
1025 | /**
1026 | * Shorthand for new Callback(create_function(...), ...);
1027 | *
1028 | * @author Tobiasz Cudnik
1029 | */
1030 | class CallbackBody extends Callback {
1031 | public function __construct($paramList, $code, $param1 = null, $param2 = null,
1032 | $param3 = null) {
1033 | $params = func_get_args();
1034 | $params = array_slice($params, 2);
1035 | $this->callback = create_function($paramList, $code);
1036 | $this->params = $params;
1037 | }
1038 | }
1039 | /**
1040 | * Callback type which on execution returns reference passed during creation.
1041 | *
1042 | * @author Tobiasz Cudnik
1043 | */
1044 | class CallbackReturnReference extends Callback
1045 | implements ICallbackNamed {
1046 | protected $reference;
1047 | public function __construct(&$reference, $name = null){
1048 | $this->reference =& $reference;
1049 | $this->callback = array($this, 'callback');
1050 | }
1051 | public function callback() {
1052 | return $this->reference;
1053 | }
1054 | public function getName() {
1055 | return 'Callback: '.$this->name;
1056 | }
1057 | public function hasName() {
1058 | return isset($this->name) && $this->name;
1059 | }
1060 | }
1061 | /**
1062 | * Callback type which on execution returns value passed during creation.
1063 | *
1064 | * @author Tobiasz Cudnik
1065 | */
1066 | class CallbackReturnValue extends Callback
1067 | implements ICallbackNamed {
1068 | protected $value;
1069 | protected $name;
1070 | public function __construct($value, $name = null){
1071 | $this->value =& $value;
1072 | $this->name = $name;
1073 | $this->callback = array($this, 'callback');
1074 | }
1075 | public function callback() {
1076 | return $this->value;
1077 | }
1078 | public function __toString() {
1079 | return $this->getName();
1080 | }
1081 | public function getName() {
1082 | return 'Callback: '.$this->name;
1083 | }
1084 | public function hasName() {
1085 | return isset($this->name) && $this->name;
1086 | }
1087 | }
1088 | /**
1089 | * CallbackParameterToReference can be used when we don't really want a callback,
1090 | * only parameter passed to it. CallbackParameterToReference takes first
1091 | * parameter's value and passes it to reference.
1092 | *
1093 | * @author Tobiasz Cudnik
1094 | */
1095 | class CallbackParameterToReference extends Callback {
1096 | /**
1097 | * @param $reference
1098 | * @TODO implement $paramIndex;
1099 | * param index choose which callback param will be passed to reference
1100 | */
1101 | public function __construct(&$reference){
1102 | $this->callback =& $reference;
1103 | }
1104 | }
1105 | //class CallbackReference extends Callback {
1106 | // /**
1107 | // *
1108 | // * @param $reference
1109 | // * @param $paramIndex
1110 | // * @todo implement $paramIndex; param index choose which callback param will be passed to reference
1111 | // */
1112 | // public function __construct(&$reference, $name = null){
1113 | // $this->callback =& $reference;
1114 | // }
1115 | //}
1116 | class CallbackParam {}
1117 |
1118 | /**
1119 | * Class representing phpQuery objects.
1120 | *
1121 | * @author Tobiasz Cudnik
1122 | * @package phpQuery
1123 | * @method phpQueryObject clone() clone()
1124 | * @method phpQueryObject empty() empty()
1125 | * @method phpQueryObject next() next($selector = null)
1126 | * @method phpQueryObject prev() prev($selector = null)
1127 | * @property Int $length
1128 | */
1129 | class phpQueryObject
1130 | implements Iterator, Countable, ArrayAccess {
1131 | public $documentID = null;
1132 | /**
1133 | * DOMDocument class.
1134 | *
1135 | * @var DOMDocument
1136 | */
1137 | public $document = null;
1138 | public $charset = null;
1139 | /**
1140 | *
1141 | * @var DOMDocumentWrapper
1142 | */
1143 | public $documentWrapper = null;
1144 | /**
1145 | * XPath interface.
1146 | *
1147 | * @var DOMXPath
1148 | */
1149 | public $xpath = null;
1150 | /**
1151 | * Stack of selected elements.
1152 | * @TODO refactor to ->nodes
1153 | * @var array
1154 | */
1155 | public $elements = array();
1156 | /**
1157 | * @access private
1158 | */
1159 | protected $elementsBackup = array();
1160 | /**
1161 | * @access private
1162 | */
1163 | protected $previous = null;
1164 | /**
1165 | * @access private
1166 | * @TODO deprecate
1167 | */
1168 | protected $root = array();
1169 | /**
1170 | * Indicated if doument is just a fragment (no tag).
1171 | *
1172 | * Every document is realy a full document, so even documentFragments can
1173 | * be queried against , but getDocument(id)->htmlOuter() will return
1174 | * only contents of .
1175 | *
1176 | * @var bool
1177 | */
1178 | public $documentFragment = true;
1179 | /**
1180 | * Iterator interface helper
1181 | * @access private
1182 | */
1183 | protected $elementsInterator = array();
1184 | /**
1185 | * Iterator interface helper
1186 | * @access private
1187 | */
1188 | protected $valid = false;
1189 | /**
1190 | * Iterator interface helper
1191 | * @access private
1192 | */
1193 | protected $current = null;
1194 | /**
1195 | * Enter description here...
1196 | *
1197 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1198 | */
1199 | public function __construct($documentID) {
1200 | // if ($documentID instanceof self)
1201 | // var_dump($documentID->getDocumentID());
1202 | $id = $documentID instanceof self
1203 | ? $documentID->getDocumentID()
1204 | : $documentID;
1205 | // var_dump($id);
1206 | if (! isset(phpQuery::$documents[$id] )) {
1207 | // var_dump(phpQuery::$documents);
1208 | throw new Exception("Document with ID '{$id}' isn't loaded. Use phpQuery::newDocument(\$html) or phpQuery::newDocumentFile(\$file) first.");
1209 | }
1210 | $this->documentID = $id;
1211 | $this->documentWrapper =& phpQuery::$documents[$id];
1212 | $this->document =& $this->documentWrapper->document;
1213 | $this->xpath =& $this->documentWrapper->xpath;
1214 | $this->charset =& $this->documentWrapper->charset;
1215 | $this->documentFragment =& $this->documentWrapper->isDocumentFragment;
1216 | // TODO check $this->DOM->documentElement;
1217 | // $this->root = $this->document->documentElement;
1218 | $this->root =& $this->documentWrapper->root;
1219 | // $this->toRoot();
1220 | $this->elements = array($this->root);
1221 | }
1222 | /**
1223 | *
1224 | * @access private
1225 | * @param $attr
1226 | * @return unknown_type
1227 | */
1228 | public function __get($attr) {
1229 | switch($attr) {
1230 | // FIXME doesnt work at all ?
1231 | case 'length':
1232 | return $this->size();
1233 | break;
1234 | default:
1235 | return $this->$attr;
1236 | }
1237 | }
1238 | /**
1239 | * Saves actual object to $var by reference.
1240 | * Useful when need to break chain.
1241 | * @param phpQueryObject $var
1242 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1243 | */
1244 | public function toReference(&$var) {
1245 | return $var = $this;
1246 | }
1247 | public function documentFragment($state = null) {
1248 | if ($state) {
1249 | phpQuery::$documents[$this->getDocumentID()]['documentFragment'] = $state;
1250 | return $this;
1251 | }
1252 | return $this->documentFragment;
1253 | }
1254 | /**
1255 | * @access private
1256 | * @TODO documentWrapper
1257 | */
1258 | protected function isRoot( $node) {
1259 | // return $node instanceof DOMDOCUMENT || $node->tagName == 'html';
1260 | return $node instanceof DOMDOCUMENT
1261 | || ($node instanceof DOMELEMENT && $node->tagName == 'html')
1262 | || $this->root->isSameNode($node);
1263 | }
1264 | /**
1265 | * @access private
1266 | */
1267 | protected function stackIsRoot() {
1268 | return $this->size() == 1 && $this->isRoot($this->elements[0]);
1269 | }
1270 | /**
1271 | * Enter description here...
1272 | * NON JQUERY METHOD
1273 | *
1274 | * Watch out, it doesn't creates new instance, can be reverted with end().
1275 | *
1276 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1277 | */
1278 | public function toRoot() {
1279 | $this->elements = array($this->root);
1280 | return $this;
1281 | // return $this->newInstance(array($this->root));
1282 | }
1283 | /**
1284 | * Saves object's DocumentID to $var by reference.
1285 | *
1286 | * $myDocumentId;
1287 | * phpQuery::newDocument('')
1288 | * ->getDocumentIDRef($myDocumentId)
1289 | * ->find('div')->...
1290 | *
1291 | *
1292 | * @param unknown_type $domId
1293 | * @see phpQuery::newDocument
1294 | * @see phpQuery::newDocumentFile
1295 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1296 | */
1297 | public function getDocumentIDRef(&$documentID) {
1298 | $documentID = $this->getDocumentID();
1299 | return $this;
1300 | }
1301 | /**
1302 | * Returns object with stack set to document root.
1303 | *
1304 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1305 | */
1306 | public function getDocument() {
1307 | return phpQuery::getDocument($this->getDocumentID());
1308 | }
1309 | /**
1310 | *
1311 | * @return DOMDocument
1312 | */
1313 | public function getDOMDocument() {
1314 | return $this->document;
1315 | }
1316 | /**
1317 | * Get object's Document ID.
1318 | *
1319 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1320 | */
1321 | public function getDocumentID() {
1322 | return $this->documentID;
1323 | }
1324 | /**
1325 | * Unloads whole document from memory.
1326 | * CAUTION! None further operations will be possible on this document.
1327 | * All objects refering to it will be useless.
1328 | *
1329 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1330 | */
1331 | public function unloadDocument() {
1332 | phpQuery::unloadDocuments($this->getDocumentID());
1333 | }
1334 | public function isHTML() {
1335 | return $this->documentWrapper->isHTML;
1336 | }
1337 | public function isXHTML() {
1338 | return $this->documentWrapper->isXHTML;
1339 | }
1340 | public function isXML() {
1341 | return $this->documentWrapper->isXML;
1342 | }
1343 | /**
1344 | * Enter description here...
1345 | *
1346 | * @link http://docs.jquery.com/Ajax/serialize
1347 | * @return string
1348 | */
1349 | public function serialize() {
1350 | return phpQuery::param($this->serializeArray());
1351 | }
1352 | /**
1353 | * Enter description here...
1354 | *
1355 | * @link http://docs.jquery.com/Ajax/serializeArray
1356 | * @return array
1357 | */
1358 | public function serializeArray($submit = null) {
1359 | $source = $this->filter('form, input, select, textarea')
1360 | ->find('input, select, textarea')
1361 | ->andSelf()
1362 | ->not('form');
1363 | $return = array();
1364 | // $source->dumpDie();
1365 | foreach($source as $input) {
1366 | $input = phpQuery::pq($input);
1367 | if ($input->is('[disabled]'))
1368 | continue;
1369 | if (!$input->is('[name]'))
1370 | continue;
1371 | if ($input->is('[type=checkbox]') && !$input->is('[checked]'))
1372 | continue;
1373 | // jquery diff
1374 | if ($submit && $input->is('[type=submit]')) {
1375 | if ($submit instanceof DOMELEMENT && ! $input->elements[0]->isSameNode($submit))
1376 | continue;
1377 | else if (is_string($submit) && $input->attr('name') != $submit)
1378 | continue;
1379 | }
1380 | $return[] = array(
1381 | 'name' => $input->attr('name'),
1382 | 'value' => $input->val(),
1383 | );
1384 | }
1385 | return $return;
1386 | }
1387 | /**
1388 | * @access private
1389 | */
1390 | protected function debug($in) {
1391 | if (! phpQuery::$debug )
1392 | return;
1393 | print('');
1394 | print_r($in);
1395 | // file debug
1396 | // file_put_contents(dirname(__FILE__).'/phpQuery.log', print_r($in, true)."\n", FILE_APPEND);
1397 | // quite handy debug trace
1398 | // if ( is_array($in))
1399 | // print_r(array_slice(debug_backtrace(), 3));
1400 | print("\n");
1401 | }
1402 | /**
1403 | * @access private
1404 | */
1405 | protected function isRegexp($pattern) {
1406 | return in_array(
1407 | $pattern[ mb_strlen($pattern)-1 ],
1408 | array('^','*','$')
1409 | );
1410 | }
1411 | /**
1412 | * Determines if $char is really a char.
1413 | *
1414 | * @param string $char
1415 | * @return bool
1416 | * @todo rewrite me to charcode range ! ;)
1417 | * @access private
1418 | */
1419 | protected function isChar($char) {
1420 | return extension_loaded('mbstring') && phpQuery::$mbstringSupport
1421 | ? mb_eregi('\w', $char)
1422 | : preg_match('@\w@', $char);
1423 | }
1424 | /**
1425 | * @access private
1426 | */
1427 | protected function parseSelector($query) {
1428 | // clean spaces
1429 | // TODO include this inside parsing ?
1430 | $query = trim(
1431 | preg_replace('@\s+@', ' ',
1432 | preg_replace('@\s*(>|\\+|~)\s*@', '\\1', $query)
1433 | )
1434 | );
1435 | $queries = array(array());
1436 | if (! $query)
1437 | return $queries;
1438 | $return =& $queries[0];
1439 | $specialChars = array('>',' ');
1440 | // $specialCharsMapping = array('/' => '>');
1441 | $specialCharsMapping = array();
1442 | $strlen = mb_strlen($query);
1443 | $classChars = array('.', '-');
1444 | $pseudoChars = array('-');
1445 | $tagChars = array('*', '|', '-');
1446 | // split multibyte string
1447 | // http://code.google.com/p/phpquery/issues/detail?id=76
1448 | $_query = array();
1449 | for ($i=0; $i<$strlen; $i++)
1450 | $_query[] = mb_substr($query, $i, 1);
1451 | $query = $_query;
1452 | // it works, but i dont like it...
1453 | $i = 0;
1454 | while( $i < $strlen) {
1455 | $c = $query[$i];
1456 | $tmp = '';
1457 | // TAG
1458 | if ($this->isChar($c) || in_array($c, $tagChars)) {
1459 | while(isset($query[$i])
1460 | && ($this->isChar($query[$i]) || in_array($query[$i], $tagChars))) {
1461 | $tmp .= $query[$i];
1462 | $i++;
1463 | }
1464 | $return[] = $tmp;
1465 | // IDs
1466 | } else if ( $c == '#') {
1467 | $i++;
1468 | while( isset($query[$i]) && ($this->isChar($query[$i]) || $query[$i] == '-')) {
1469 | $tmp .= $query[$i];
1470 | $i++;
1471 | }
1472 | $return[] = '#'.$tmp;
1473 | // SPECIAL CHARS
1474 | } else if (in_array($c, $specialChars)) {
1475 | $return[] = $c;
1476 | $i++;
1477 | // MAPPED SPECIAL MULTICHARS
1478 | // } else if ( $c.$query[$i+1] == '//') {
1479 | // $return[] = ' ';
1480 | // $i = $i+2;
1481 | // MAPPED SPECIAL CHARS
1482 | } else if ( isset($specialCharsMapping[$c])) {
1483 | $return[] = $specialCharsMapping[$c];
1484 | $i++;
1485 | // COMMA
1486 | } else if ( $c == ',') {
1487 | $queries[] = array();
1488 | $return =& $queries[ count($queries)-1 ];
1489 | $i++;
1490 | while( isset($query[$i]) && $query[$i] == ' ')
1491 | $i++;
1492 | // CLASSES
1493 | } else if ($c == '.') {
1494 | while( isset($query[$i]) && ($this->isChar($query[$i]) || in_array($query[$i], $classChars))) {
1495 | $tmp .= $query[$i];
1496 | $i++;
1497 | }
1498 | $return[] = $tmp;
1499 | // ~ General Sibling Selector
1500 | } else if ($c == '~') {
1501 | $spaceAllowed = true;
1502 | $tmp .= $query[$i++];
1503 | while( isset($query[$i])
1504 | && ($this->isChar($query[$i])
1505 | || in_array($query[$i], $classChars)
1506 | || $query[$i] == '*'
1507 | || ($query[$i] == ' ' && $spaceAllowed)
1508 | )) {
1509 | if ($query[$i] != ' ')
1510 | $spaceAllowed = false;
1511 | $tmp .= $query[$i];
1512 | $i++;
1513 | }
1514 | $return[] = $tmp;
1515 | // + Adjacent sibling selectors
1516 | } else if ($c == '+') {
1517 | $spaceAllowed = true;
1518 | $tmp .= $query[$i++];
1519 | while( isset($query[$i])
1520 | && ($this->isChar($query[$i])
1521 | || in_array($query[$i], $classChars)
1522 | || $query[$i] == '*'
1523 | || ($spaceAllowed && $query[$i] == ' ')
1524 | )) {
1525 | if ($query[$i] != ' ')
1526 | $spaceAllowed = false;
1527 | $tmp .= $query[$i];
1528 | $i++;
1529 | }
1530 | $return[] = $tmp;
1531 | // ATTRS
1532 | } else if ($c == '[') {
1533 | $stack = 1;
1534 | $tmp .= $c;
1535 | while( isset($query[++$i])) {
1536 | $tmp .= $query[$i];
1537 | if ( $query[$i] == '[') {
1538 | $stack++;
1539 | } else if ( $query[$i] == ']') {
1540 | $stack--;
1541 | if (! $stack )
1542 | break;
1543 | }
1544 | }
1545 | $return[] = $tmp;
1546 | $i++;
1547 | // PSEUDO CLASSES
1548 | } else if ($c == ':') {
1549 | $stack = 1;
1550 | $tmp .= $query[$i++];
1551 | while( isset($query[$i]) && ($this->isChar($query[$i]) || in_array($query[$i], $pseudoChars))) {
1552 | $tmp .= $query[$i];
1553 | $i++;
1554 | }
1555 | // with arguments ?
1556 | if ( isset($query[$i]) && $query[$i] == '(') {
1557 | $tmp .= $query[$i];
1558 | $stack = 1;
1559 | while( isset($query[++$i])) {
1560 | $tmp .= $query[$i];
1561 | if ( $query[$i] == '(') {
1562 | $stack++;
1563 | } else if ( $query[$i] == ')') {
1564 | $stack--;
1565 | if (! $stack )
1566 | break;
1567 | }
1568 | }
1569 | $return[] = $tmp;
1570 | $i++;
1571 | } else {
1572 | $return[] = $tmp;
1573 | }
1574 | } else {
1575 | $i++;
1576 | }
1577 | }
1578 | foreach($queries as $k => $q) {
1579 | if (isset($q[0])) {
1580 | if (isset($q[0][0]) && $q[0][0] == ':')
1581 | array_unshift($queries[$k], '*');
1582 | if ($q[0] != '>')
1583 | array_unshift($queries[$k], ' ');
1584 | }
1585 | }
1586 | return $queries;
1587 | }
1588 | /**
1589 | * Return matched DOM nodes.
1590 | *
1591 | * @param int $index
1592 | * @return array|DOMElement Single DOMElement or array of DOMElement.
1593 | */
1594 | public function get($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1595 | $return = isset($index)
1596 | ? (isset($this->elements[$index]) ? $this->elements[$index] : null)
1597 | : $this->elements;
1598 | // pass thou callbacks
1599 | $args = func_get_args();
1600 | $args = array_slice($args, 1);
1601 | foreach($args as $callback) {
1602 | if (is_array($return))
1603 | foreach($return as $k => $v)
1604 | $return[$k] = phpQuery::callbackRun($callback, array($v));
1605 | else
1606 | $return = phpQuery::callbackRun($callback, array($return));
1607 | }
1608 | return $return;
1609 | }
1610 | /**
1611 | * Return matched DOM nodes.
1612 | * jQuery difference.
1613 | *
1614 | * @param int $index
1615 | * @return array|string Returns string if $index != null
1616 | * @todo implement callbacks
1617 | * @todo return only arrays ?
1618 | * @todo maybe other name...
1619 | */
1620 | public function getString($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1621 | if ($index)
1622 | $return = $this->eq($index)->text();
1623 | else {
1624 | $return = array();
1625 | for($i = 0; $i < $this->size(); $i++) {
1626 | $return[] = $this->eq($i)->text();
1627 | }
1628 | }
1629 | // pass thou callbacks
1630 | $args = func_get_args();
1631 | $args = array_slice($args, 1);
1632 | foreach($args as $callback) {
1633 | $return = phpQuery::callbackRun($callback, array($return));
1634 | }
1635 | return $return;
1636 | }
1637 | /**
1638 | * Return matched DOM nodes.
1639 | * jQuery difference.
1640 | *
1641 | * @param int $index
1642 | * @return array|string Returns string if $index != null
1643 | * @todo implement callbacks
1644 | * @todo return only arrays ?
1645 | * @todo maybe other name...
1646 | */
1647 | public function getStrings($index = null, $callback1 = null, $callback2 = null, $callback3 = null) {
1648 | if ($index)
1649 | $return = $this->eq($index)->text();
1650 | else {
1651 | $return = array();
1652 | for($i = 0; $i < $this->size(); $i++) {
1653 | $return[] = $this->eq($i)->text();
1654 | }
1655 | // pass thou callbacks
1656 | $args = func_get_args();
1657 | $args = array_slice($args, 1);
1658 | }
1659 | foreach($args as $callback) {
1660 | if (is_array($return))
1661 | foreach($return as $k => $v)
1662 | $return[$k] = phpQuery::callbackRun($callback, array($v));
1663 | else
1664 | $return = phpQuery::callbackRun($callback, array($return));
1665 | }
1666 | return $return;
1667 | }
1668 | /**
1669 | * Returns new instance of actual class.
1670 | *
1671 | * @param array $newStack Optional. Will replace old stack with new and move old one to history.c
1672 | */
1673 | public function newInstance($newStack = null) {
1674 | $class = get_class($this);
1675 | // support inheritance by passing old object to overloaded constructor
1676 | $new = $class != 'phpQuery'
1677 | ? new $class($this, $this->getDocumentID())
1678 | : new phpQueryObject($this->getDocumentID());
1679 | $new->previous = $this;
1680 | if (is_null($newStack)) {
1681 | $new->elements = $this->elements;
1682 | if ($this->elementsBackup)
1683 | $this->elements = $this->elementsBackup;
1684 | } else if (is_string($newStack)) {
1685 | $new->elements = phpQuery::pq($newStack, $this->getDocumentID())->stack();
1686 | } else {
1687 | $new->elements = $newStack;
1688 | }
1689 | return $new;
1690 | }
1691 | /**
1692 | * Enter description here...
1693 | *
1694 | * In the future, when PHP will support XLS 2.0, then we would do that this way:
1695 | * contains(tokenize(@class, '\s'), "something")
1696 | * @param unknown_type $class
1697 | * @param unknown_type $node
1698 | * @return boolean
1699 | * @access private
1700 | */
1701 | protected function matchClasses($class, $node) {
1702 | // multi-class
1703 | if ( mb_strpos($class, '.', 1)) {
1704 | $classes = explode('.', substr($class, 1));
1705 | $classesCount = count( $classes );
1706 | $nodeClasses = explode(' ', $node->getAttribute('class') );
1707 | $nodeClassesCount = count( $nodeClasses );
1708 | if ( $classesCount > $nodeClassesCount )
1709 | return false;
1710 | $diff = count(
1711 | array_diff(
1712 | $classes,
1713 | $nodeClasses
1714 | )
1715 | );
1716 | if (! $diff )
1717 | return true;
1718 | // single-class
1719 | } else {
1720 | return in_array(
1721 | // strip leading dot from class name
1722 | substr($class, 1),
1723 | // get classes for element as array
1724 | explode(' ', $node->getAttribute('class') )
1725 | );
1726 | }
1727 | }
1728 | /**
1729 | * @access private
1730 | */
1731 | protected function runQuery($XQuery, $selector = null, $compare = null) {
1732 | if ($compare && ! method_exists($this, $compare))
1733 | return false;
1734 | $stack = array();
1735 | if (! $this->elements)
1736 | $this->debug('Stack empty, skipping...');
1737 | // var_dump($this->elements[0]->nodeType);
1738 | // element, document
1739 | foreach($this->stack(array(1, 9, 13)) as $k => $stackNode) {
1740 | $detachAfter = false;
1741 | // to work on detached nodes we need temporary place them somewhere
1742 | // thats because context xpath queries sucks ;]
1743 | $testNode = $stackNode;
1744 | while ($testNode) {
1745 | if (! $testNode->parentNode && ! $this->isRoot($testNode)) {
1746 | $this->root->appendChild($testNode);
1747 | $detachAfter = $testNode;
1748 | break;
1749 | }
1750 | $testNode = isset($testNode->parentNode)
1751 | ? $testNode->parentNode
1752 | : null;
1753 | }
1754 | // XXX tmp ?
1755 | $xpath = $this->documentWrapper->isXHTML
1756 | ? $this->getNodeXpath($stackNode, 'html')
1757 | : $this->getNodeXpath($stackNode);
1758 | // FIXME pseudoclasses-only query, support XML
1759 | $query = $XQuery == '//' && $xpath == '/html[1]'
1760 | ? '//*'
1761 | : $xpath.$XQuery;
1762 | $this->debug("XPATH: {$query}");
1763 | // run query, get elements
1764 | $nodes = $this->xpath->query($query);
1765 | $this->debug("QUERY FETCHED");
1766 | if (! $nodes->length )
1767 | $this->debug('Nothing found');
1768 | $debug = array();
1769 | foreach($nodes as $node) {
1770 | $matched = false;
1771 | if ( $compare) {
1772 | phpQuery::$debug ?
1773 | $this->debug("Found: ".$this->whois( $node ).", comparing with {$compare}()")
1774 | : null;
1775 | $phpQueryDebug = phpQuery::$debug;
1776 | phpQuery::$debug = false;
1777 | // TODO ??? use phpQuery::callbackRun()
1778 | if (call_user_func_array(array($this, $compare), array($selector, $node)))
1779 | $matched = true;
1780 | phpQuery::$debug = $phpQueryDebug;
1781 | } else {
1782 | $matched = true;
1783 | }
1784 | if ( $matched) {
1785 | if (phpQuery::$debug)
1786 | $debug[] = $this->whois( $node );
1787 | $stack[] = $node;
1788 | }
1789 | }
1790 | if (phpQuery::$debug) {
1791 | $this->debug("Matched ".count($debug).": ".implode(', ', $debug));
1792 | }
1793 | if ($detachAfter)
1794 | $this->root->removeChild($detachAfter);
1795 | }
1796 | $this->elements = $stack;
1797 | }
1798 | /**
1799 | * Enter description here...
1800 | *
1801 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
1802 | */
1803 | public function find($selectors, $context = null, $noHistory = false) {
1804 | if (!$noHistory)
1805 | // backup last stack /for end()/
1806 | $this->elementsBackup = $this->elements;
1807 | // allow to define context
1808 | // TODO combine code below with phpQuery::pq() context guessing code
1809 | // as generic function
1810 | if ($context) {
1811 | if (! is_array($context) && $context instanceof DOMELEMENT)
1812 | $this->elements = array($context);
1813 | else if (is_array($context)) {
1814 | $this->elements = array();
1815 | foreach ($context as $c)
1816 | if ($c instanceof DOMELEMENT)
1817 | $this->elements[] = $c;
1818 | } else if ( $context instanceof self )
1819 | $this->elements = $context->elements;
1820 | }
1821 | $queries = $this->parseSelector($selectors);
1822 | $this->debug(array('FIND', $selectors, $queries));
1823 | $XQuery = '';
1824 | // remember stack state because of multi-queries
1825 | $oldStack = $this->elements;
1826 | // here we will be keeping found elements
1827 | $stack = array();
1828 | foreach($queries as $selector) {
1829 | $this->elements = $oldStack;
1830 | $delimiterBefore = false;
1831 | foreach($selector as $s) {
1832 | // TAG
1833 | $isTag = extension_loaded('mbstring') && phpQuery::$mbstringSupport
1834 | ? mb_ereg_match('^[\w|\||-]+$', $s) || $s == '*'
1835 | : preg_match('@^[\w|\||-]+$@', $s) || $s == '*';
1836 | if ($isTag) {
1837 | if ($this->isXML()) {
1838 | // namespace support
1839 | if (mb_strpos($s, '|') !== false) {
1840 | $ns = $tag = null;
1841 | list($ns, $tag) = explode('|', $s);
1842 | $XQuery .= "$ns:$tag";
1843 | } else if ($s == '*') {
1844 | $XQuery .= "*";
1845 | } else {
1846 | $XQuery .= "*[local-name()='$s']";
1847 | }
1848 | } else {
1849 | $XQuery .= $s;
1850 | }
1851 | // ID
1852 | } else if ($s[0] == '#') {
1853 | if ($delimiterBefore)
1854 | $XQuery .= '*';
1855 | $XQuery .= "[@id='".substr($s, 1)."']";
1856 | // ATTRIBUTES
1857 | } else if ($s[0] == '[') {
1858 | if ($delimiterBefore)
1859 | $XQuery .= '*';
1860 | // strip side brackets
1861 | $attr = trim($s, '][');
1862 | $execute = false;
1863 | // attr with specifed value
1864 | if (mb_strpos($s, '=')) {
1865 | $value = null;
1866 | list($attr, $value) = explode('=', $attr);
1867 | $value = trim($value, "'\"");
1868 | if ($this->isRegexp($attr)) {
1869 | // cut regexp character
1870 | $attr = substr($attr, 0, -1);
1871 | $execute = true;
1872 | $XQuery .= "[@{$attr}]";
1873 | } else {
1874 | $XQuery .= "[@{$attr}='{$value}']";
1875 | }
1876 | // attr without specified value
1877 | } else {
1878 | $XQuery .= "[@{$attr}]";
1879 | }
1880 | if ($execute) {
1881 | $this->runQuery($XQuery, $s, 'is');
1882 | $XQuery = '';
1883 | if (! $this->length())
1884 | break;
1885 | }
1886 | // CLASSES
1887 | } else if ($s[0] == '.') {
1888 | // TODO use return $this->find("./self::*[contains(concat(\" \",@class,\" \"), \" $class \")]");
1889 | // thx wizDom ;)
1890 | if ($delimiterBefore)
1891 | $XQuery .= '*';
1892 | $XQuery .= '[@class]';
1893 | $this->runQuery($XQuery, $s, 'matchClasses');
1894 | $XQuery = '';
1895 | if (! $this->length() )
1896 | break;
1897 | // ~ General Sibling Selector
1898 | } else if ($s[0] == '~') {
1899 | $this->runQuery($XQuery);
1900 | $XQuery = '';
1901 | $this->elements = $this
1902 | ->siblings(
1903 | substr($s, 1)
1904 | )->elements;
1905 | if (! $this->length() )
1906 | break;
1907 | // + Adjacent sibling selectors
1908 | } else if ($s[0] == '+') {
1909 | // TODO /following-sibling::
1910 | $this->runQuery($XQuery);
1911 | $XQuery = '';
1912 | $subSelector = substr($s, 1);
1913 | $subElements = $this->elements;
1914 | $this->elements = array();
1915 | foreach($subElements as $node) {
1916 | // search first DOMElement sibling
1917 | $test = $node->nextSibling;
1918 | while($test && ! ($test instanceof DOMELEMENT))
1919 | $test = $test->nextSibling;
1920 | if ($test && $this->is($subSelector, $test))
1921 | $this->elements[] = $test;
1922 | }
1923 | if (! $this->length() )
1924 | break;
1925 | // PSEUDO CLASSES
1926 | } else if ($s[0] == ':') {
1927 | // TODO optimization for :first :last
1928 | if ($XQuery) {
1929 | $this->runQuery($XQuery);
1930 | $XQuery = '';
1931 | }
1932 | if (! $this->length())
1933 | break;
1934 | $this->pseudoClasses($s);
1935 | if (! $this->length())
1936 | break;
1937 | // DIRECT DESCENDANDS
1938 | } else if ($s == '>') {
1939 | $XQuery .= '/';
1940 | $delimiterBefore = 2;
1941 | // ALL DESCENDANDS
1942 | } else if ($s == ' ') {
1943 | $XQuery .= '//';
1944 | $delimiterBefore = 2;
1945 | // ERRORS
1946 | } else {
1947 | phpQuery::debug("Unrecognized token '$s'");
1948 | }
1949 | $delimiterBefore = $delimiterBefore === 2;
1950 | }
1951 | // run query if any
1952 | if ($XQuery && $XQuery != '//') {
1953 | $this->runQuery($XQuery);
1954 | $XQuery = '';
1955 | }
1956 | foreach($this->elements as $node)
1957 | if (! $this->elementsContainsNode($node, $stack))
1958 | $stack[] = $node;
1959 | }
1960 | $this->elements = $stack;
1961 | return $this->newInstance();
1962 | }
1963 | /**
1964 | * @todo create API for classes with pseudoselectors
1965 | * @access private
1966 | */
1967 | protected function pseudoClasses($class) {
1968 | // TODO clean args parsing ?
1969 | $class = ltrim($class, ':');
1970 | $haveArgs = mb_strpos($class, '(');
1971 | if ($haveArgs !== false) {
1972 | $args = substr($class, $haveArgs+1, -1);
1973 | $class = substr($class, 0, $haveArgs);
1974 | }
1975 | switch($class) {
1976 | case 'even':
1977 | case 'odd':
1978 | $stack = array();
1979 | foreach($this->elements as $i => $node) {
1980 | if ($class == 'even' && ($i%2) == 0)
1981 | $stack[] = $node;
1982 | else if ( $class == 'odd' && $i % 2 )
1983 | $stack[] = $node;
1984 | }
1985 | $this->elements = $stack;
1986 | break;
1987 | case 'eq':
1988 | $k = intval($args);
1989 | $this->elements = isset( $this->elements[$k] )
1990 | ? array( $this->elements[$k] )
1991 | : array();
1992 | break;
1993 | case 'gt':
1994 | $this->elements = array_slice($this->elements, $args+1);
1995 | break;
1996 | case 'lt':
1997 | $this->elements = array_slice($this->elements, 0, $args+1);
1998 | break;
1999 | case 'first':
2000 | if (isset($this->elements[0]))
2001 | $this->elements = array($this->elements[0]);
2002 | break;
2003 | case 'last':
2004 | if ($this->elements)
2005 | $this->elements = array($this->elements[count($this->elements)-1]);
2006 | break;
2007 | /*case 'parent':
2008 | $stack = array();
2009 | foreach($this->elements as $node) {
2010 | if ( $node->childNodes->length )
2011 | $stack[] = $node;
2012 | }
2013 | $this->elements = $stack;
2014 | break;*/
2015 | case 'contains':
2016 | $text = trim($args, "\"'");
2017 | $stack = array();
2018 | foreach($this->elements as $node) {
2019 | if (mb_stripos($node->textContent, $text) === false)
2020 | continue;
2021 | $stack[] = $node;
2022 | }
2023 | $this->elements = $stack;
2024 | break;
2025 | case 'not':
2026 | $selector = self::unQuote($args);
2027 | $this->elements = $this->not($selector)->stack();
2028 | break;
2029 | case 'slice':
2030 | // TODO jQuery difference ?
2031 | $args = explode(',',
2032 | str_replace(', ', ',', trim($args, "\"'"))
2033 | );
2034 | $start = $args[0];
2035 | $end = isset($args[1])
2036 | ? $args[1]
2037 | : null;
2038 | if ($end > 0)
2039 | $end = $end-$start;
2040 | $this->elements = array_slice($this->elements, $start, $end);
2041 | break;
2042 | case 'has':
2043 | $selector = trim($args, "\"'");
2044 | $stack = array();
2045 | foreach($this->stack(1) as $el) {
2046 | if ($this->find($selector, $el, true)->length)
2047 | $stack[] = $el;
2048 | }
2049 | $this->elements = $stack;
2050 | break;
2051 | case 'submit':
2052 | case 'reset':
2053 | $this->elements = phpQuery::merge(
2054 | $this->map(array($this, 'is'),
2055 | "input[type=$class]", new CallbackParam()
2056 | ),
2057 | $this->map(array($this, 'is'),
2058 | "button[type=$class]", new CallbackParam()
2059 | )
2060 | );
2061 | break;
2062 | // $stack = array();
2063 | // foreach($this->elements as $node)
2064 | // if ($node->is('input[type=submit]') || $node->is('button[type=submit]'))
2065 | // $stack[] = $el;
2066 | // $this->elements = $stack;
2067 | case 'input':
2068 | $this->elements = $this->map(
2069 | array($this, 'is'),
2070 | 'input', new CallbackParam()
2071 | )->elements;
2072 | break;
2073 | case 'password':
2074 | case 'checkbox':
2075 | case 'radio':
2076 | case 'hidden':
2077 | case 'image':
2078 | case 'file':
2079 | $this->elements = $this->map(
2080 | array($this, 'is'),
2081 | "input[type=$class]", new CallbackParam()
2082 | )->elements;
2083 | break;
2084 | case 'parent':
2085 | $this->elements = $this->map(
2086 | create_function('$node', '
2087 | return $node instanceof DOMELEMENT && $node->childNodes->length
2088 | ? $node : null;')
2089 | )->elements;
2090 | break;
2091 | case 'empty':
2092 | $this->elements = $this->map(
2093 | create_function('$node', '
2094 | return $node instanceof DOMELEMENT && $node->childNodes->length
2095 | ? null : $node;')
2096 | )->elements;
2097 | break;
2098 | case 'disabled':
2099 | case 'selected':
2100 | case 'checked':
2101 | $this->elements = $this->map(
2102 | array($this, 'is'),
2103 | "[$class]", new CallbackParam()
2104 | )->elements;
2105 | break;
2106 | case 'enabled':
2107 | $this->elements = $this->map(
2108 | create_function('$node', '
2109 | return pq($node)->not(":disabled") ? $node : null;')
2110 | )->elements;
2111 | break;
2112 | case 'header':
2113 | $this->elements = $this->map(
2114 | create_function('$node',
2115 | '$isHeader = isset($node->tagName) && in_array($node->tagName, array(
2116 | "h1", "h2", "h3", "h4", "h5", "h6", "h7"
2117 | ));
2118 | return $isHeader
2119 | ? $node
2120 | : null;')
2121 | )->elements;
2122 | // $this->elements = $this->map(
2123 | // create_function('$node', '$node = pq($node);
2124 | // return $node->is("h1")
2125 | // || $node->is("h2")
2126 | // || $node->is("h3")
2127 | // || $node->is("h4")
2128 | // || $node->is("h5")
2129 | // || $node->is("h6")
2130 | // || $node->is("h7")
2131 | // ? $node
2132 | // : null;')
2133 | // )->elements;
2134 | break;
2135 | case 'only-child':
2136 | $this->elements = $this->map(
2137 | create_function('$node',
2138 | 'return pq($node)->siblings()->size() == 0 ? $node : null;')
2139 | )->elements;
2140 | break;
2141 | case 'first-child':
2142 | $this->elements = $this->map(
2143 | create_function('$node', 'return pq($node)->prevAll()->size() == 0 ? $node : null;')
2144 | )->elements;
2145 | break;
2146 | case 'last-child':
2147 | $this->elements = $this->map(
2148 | create_function('$node', 'return pq($node)->nextAll()->size() == 0 ? $node : null;')
2149 | )->elements;
2150 | break;
2151 | case 'nth-child':
2152 | $param = trim($args, "\"'");
2153 | if (! $param)
2154 | break;
2155 | // nth-child(n+b) to nth-child(1n+b)
2156 | if ($param{0} == 'n')
2157 | $param = '1'.$param;
2158 | // :nth-child(index/even/odd/equation)
2159 | if ($param == 'even' || $param == 'odd')
2160 | $mapped = $this->map(
2161 | create_function('$node, $param',
2162 | '$index = pq($node)->prevAll()->size()+1;
2163 | if ($param == "even" && ($index%2) == 0)
2164 | return $node;
2165 | else if ($param == "odd" && $index%2 == 1)
2166 | return $node;
2167 | else
2168 | return null;'),
2169 | new CallbackParam(), $param
2170 | );
2171 | else if (mb_strlen($param) > 1 && $param{1} == 'n')
2172 | // an+b
2173 | $mapped = $this->map(
2174 | create_function('$node, $param',
2175 | '$prevs = pq($node)->prevAll()->size();
2176 | $index = 1+$prevs;
2177 | $b = mb_strlen($param) > 3
2178 | ? $param{3}
2179 | : 0;
2180 | $a = $param{0};
2181 | if ($b && $param{2} == "-")
2182 | $b = -$b;
2183 | if ($a > 0) {
2184 | return ($index-$b)%$a == 0
2185 | ? $node
2186 | : null;
2187 | phpQuery::debug($a."*".floor($index/$a)."+$b-1 == ".($a*floor($index/$a)+$b-1)." ?= $prevs");
2188 | return $a*floor($index/$a)+$b-1 == $prevs
2189 | ? $node
2190 | : null;
2191 | } else if ($a == 0)
2192 | return $index == $b
2193 | ? $node
2194 | : null;
2195 | else
2196 | // negative value
2197 | return $index <= $b
2198 | ? $node
2199 | : null;
2200 | // if (! $b)
2201 | // return $index%$a == 0
2202 | // ? $node
2203 | // : null;
2204 | // else
2205 | // return ($index-$b)%$a == 0
2206 | // ? $node
2207 | // : null;
2208 | '),
2209 | new CallbackParam(), $param
2210 | );
2211 | else
2212 | // index
2213 | $mapped = $this->map(
2214 | create_function('$node, $index',
2215 | '$prevs = pq($node)->prevAll()->size();
2216 | if ($prevs && $prevs == $index-1)
2217 | return $node;
2218 | else if (! $prevs && $index == 1)
2219 | return $node;
2220 | else
2221 | return null;'),
2222 | new CallbackParam(), $param
2223 | );
2224 | $this->elements = $mapped->elements;
2225 | break;
2226 | default:
2227 | $this->debug("Unknown pseudoclass '{$class}', skipping...");
2228 | }
2229 | }
2230 | /**
2231 | * @access private
2232 | */
2233 | protected function __pseudoClassParam($paramsString) {
2234 | // TODO;
2235 | }
2236 | /**
2237 | * Enter description here...
2238 | *
2239 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2240 | */
2241 | public function is($selector, $nodes = null) {
2242 | phpQuery::debug(array("Is:", $selector));
2243 | if (! $selector)
2244 | return false;
2245 | $oldStack = $this->elements;
2246 | $returnArray = false;
2247 | if ($nodes && is_array($nodes)) {
2248 | $this->elements = $nodes;
2249 | } else if ($nodes)
2250 | $this->elements = array($nodes);
2251 | $this->filter($selector, true);
2252 | $stack = $this->elements;
2253 | $this->elements = $oldStack;
2254 | if ($nodes)
2255 | return $stack ? $stack : null;
2256 | return (bool)count($stack);
2257 | }
2258 | /**
2259 | * Enter description here...
2260 | * jQuery difference.
2261 | *
2262 | * Callback:
2263 | * - $index int
2264 | * - $node DOMNode
2265 | *
2266 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2267 | * @link http://docs.jquery.com/Traversing/filter
2268 | */
2269 | public function filterCallback($callback, $_skipHistory = false) {
2270 | if (! $_skipHistory) {
2271 | $this->elementsBackup = $this->elements;
2272 | $this->debug("Filtering by callback");
2273 | }
2274 | $newStack = array();
2275 | foreach($this->elements as $index => $node) {
2276 | $result = phpQuery::callbackRun($callback, array($index, $node));
2277 | if (is_null($result) || (! is_null($result) && $result))
2278 | $newStack[] = $node;
2279 | }
2280 | $this->elements = $newStack;
2281 | return $_skipHistory
2282 | ? $this
2283 | : $this->newInstance();
2284 | }
2285 | /**
2286 | * Enter description here...
2287 | *
2288 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2289 | * @link http://docs.jquery.com/Traversing/filter
2290 | */
2291 | public function filter($selectors, $_skipHistory = false) {
2292 | if ($selectors instanceof Callback OR $selectors instanceof Closure)
2293 | return $this->filterCallback($selectors, $_skipHistory);
2294 | if (! $_skipHistory)
2295 | $this->elementsBackup = $this->elements;
2296 | $notSimpleSelector = array(' ', '>', '~', '+', '/');
2297 | if (! is_array($selectors))
2298 | $selectors = $this->parseSelector($selectors);
2299 | if (! $_skipHistory)
2300 | $this->debug(array("Filtering:", $selectors));
2301 | $finalStack = array();
2302 | foreach($selectors as $selector) {
2303 | $stack = array();
2304 | if (! $selector)
2305 | break;
2306 | // avoid first space or /
2307 | if (in_array($selector[0], $notSimpleSelector))
2308 | $selector = array_slice($selector, 1);
2309 | // PER NODE selector chunks
2310 | foreach($this->stack() as $node) {
2311 | $break = false;
2312 | foreach($selector as $s) {
2313 | if (!($node instanceof DOMELEMENT)) {
2314 | // all besides DOMElement
2315 | if ( $s[0] == '[') {
2316 | $attr = trim($s, '[]');
2317 | if ( mb_strpos($attr, '=')) {
2318 | list( $attr, $val ) = explode('=', $attr);
2319 | if ($attr == 'nodeType' && $node->nodeType != $val)
2320 | $break = true;
2321 | }
2322 | } else
2323 | $break = true;
2324 | } else {
2325 | // DOMElement only
2326 | // ID
2327 | if ( $s[0] == '#') {
2328 | if ( $node->getAttribute('id') != substr($s, 1) )
2329 | $break = true;
2330 | // CLASSES
2331 | } else if ( $s[0] == '.') {
2332 | if (! $this->matchClasses( $s, $node ) )
2333 | $break = true;
2334 | // ATTRS
2335 | } else if ( $s[0] == '[') {
2336 | // strip side brackets
2337 | $attr = trim($s, '[]');
2338 | if (mb_strpos($attr, '=')) {
2339 | list($attr, $val) = explode('=', $attr);
2340 | $val = self::unQuote($val);
2341 | if ($attr == 'nodeType') {
2342 | if ($val != $node->nodeType)
2343 | $break = true;
2344 | } else if ($this->isRegexp($attr)) {
2345 | $val = extension_loaded('mbstring') && phpQuery::$mbstringSupport
2346 | ? quotemeta(trim($val, '"\''))
2347 | : preg_quote(trim($val, '"\''), '@');
2348 | // switch last character
2349 | switch( substr($attr, -1)) {
2350 | // quotemeta used insted of preg_quote
2351 | // http://code.google.com/p/phpquery/issues/detail?id=76
2352 | case '^':
2353 | $pattern = '^'.$val;
2354 | break;
2355 | case '*':
2356 | $pattern = '.*'.$val.'.*';
2357 | break;
2358 | case '$':
2359 | $pattern = '.*'.$val.'$';
2360 | break;
2361 | }
2362 | // cut last character
2363 | $attr = substr($attr, 0, -1);
2364 | $isMatch = extension_loaded('mbstring') && phpQuery::$mbstringSupport
2365 | ? mb_ereg_match($pattern, $node->getAttribute($attr))
2366 | : preg_match("@{$pattern}@", $node->getAttribute($attr));
2367 | if (! $isMatch)
2368 | $break = true;
2369 | } else if ($node->getAttribute($attr) != $val)
2370 | $break = true;
2371 | } else if (! $node->hasAttribute($attr))
2372 | $break = true;
2373 | // PSEUDO CLASSES
2374 | } else if ( $s[0] == ':') {
2375 | // skip
2376 | // TAG
2377 | } else if (trim($s)) {
2378 | if ($s != '*') {
2379 | // TODO namespaces
2380 | if (isset($node->tagName)) {
2381 | if ($node->tagName != $s)
2382 | $break = true;
2383 | } else if ($s == 'html' && ! $this->isRoot($node))
2384 | $break = true;
2385 | }
2386 | // AVOID NON-SIMPLE SELECTORS
2387 | } else if (in_array($s, $notSimpleSelector)) {
2388 | $break = true;
2389 | $this->debug(array('Skipping non simple selector', $selector));
2390 | }
2391 | }
2392 | if ($break)
2393 | break;
2394 | }
2395 | // if element passed all chunks of selector - add it to new stack
2396 | if (! $break )
2397 | $stack[] = $node;
2398 | }
2399 | $tmpStack = $this->elements;
2400 | $this->elements = $stack;
2401 | // PER ALL NODES selector chunks
2402 | foreach($selector as $s)
2403 | // PSEUDO CLASSES
2404 | if ($s[0] == ':')
2405 | $this->pseudoClasses($s);
2406 | foreach($this->elements as $node)
2407 | // XXX it should be merged without duplicates
2408 | // but jQuery doesnt do that
2409 | $finalStack[] = $node;
2410 | $this->elements = $tmpStack;
2411 | }
2412 | $this->elements = $finalStack;
2413 | if ($_skipHistory) {
2414 | return $this;
2415 | } else {
2416 | $this->debug("Stack length after filter(): ".count($finalStack));
2417 | return $this->newInstance();
2418 | }
2419 | }
2420 | /**
2421 | *
2422 | * @param $value
2423 | * @return unknown_type
2424 | * @TODO implement in all methods using passed parameters
2425 | */
2426 | protected static function unQuote($value) {
2427 | return $value[0] == '\'' || $value[0] == '"'
2428 | ? substr($value, 1, -1)
2429 | : $value;
2430 | }
2431 | /**
2432 | * Enter description here...
2433 | *
2434 | * @link http://docs.jquery.com/Ajax/load
2435 | * @return phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2436 | * @todo Support $selector
2437 | */
2438 | public function load($url, $data = null, $callback = null) {
2439 | if ($data && ! is_array($data)) {
2440 | $callback = $data;
2441 | $data = null;
2442 | }
2443 | if (mb_strpos($url, ' ') !== false) {
2444 | $matches = null;
2445 | if (extension_loaded('mbstring') && phpQuery::$mbstringSupport)
2446 | mb_ereg('^([^ ]+) (.*)$', $url, $matches);
2447 | else
2448 | preg_match('^([^ ]+) (.*)$', $url, $matches);
2449 | $url = $matches[1];
2450 | $selector = $matches[2];
2451 | // FIXME this sucks, pass as callback param
2452 | $this->_loadSelector = $selector;
2453 | }
2454 | $ajax = array(
2455 | 'url' => $url,
2456 | 'type' => $data ? 'POST' : 'GET',
2457 | 'data' => $data,
2458 | 'complete' => $callback,
2459 | 'success' => array($this, '__loadSuccess')
2460 | );
2461 | phpQuery::ajax($ajax);
2462 | return $this;
2463 | }
2464 | /**
2465 | * @access private
2466 | * @param $html
2467 | * @return unknown_type
2468 | */
2469 | public function __loadSuccess($html) {
2470 | if ($this->_loadSelector) {
2471 | $html = phpQuery::newDocument($html)->find($this->_loadSelector);
2472 | unset($this->_loadSelector);
2473 | }
2474 | foreach($this->stack(1) as $node) {
2475 | phpQuery::pq($node, $this->getDocumentID())
2476 | ->markup($html);
2477 | }
2478 | }
2479 | /**
2480 | * Enter description here...
2481 | *
2482 | * @return phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2483 | * @todo
2484 | */
2485 | public function css() {
2486 | // TODO
2487 | return $this;
2488 | }
2489 | /**
2490 | * @todo
2491 | *
2492 | */
2493 | public function show(){
2494 | // TODO
2495 | return $this;
2496 | }
2497 | /**
2498 | * @todo
2499 | *
2500 | */
2501 | public function hide(){
2502 | // TODO
2503 | return $this;
2504 | }
2505 | /**
2506 | * Trigger a type of event on every matched element.
2507 | *
2508 | * @param unknown_type $type
2509 | * @param unknown_type $data
2510 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2511 | * @TODO support more than event in $type (space-separated)
2512 | */
2513 | public function trigger($type, $data = array()) {
2514 | foreach($this->elements as $node)
2515 | phpQueryEvents::trigger($this->getDocumentID(), $type, $data, $node);
2516 | return $this;
2517 | }
2518 | /**
2519 | * This particular method triggers all bound event handlers on an element (for a specific event type) WITHOUT executing the browsers default actions.
2520 | *
2521 | * @param unknown_type $type
2522 | * @param unknown_type $data
2523 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2524 | * @TODO
2525 | */
2526 | public function triggerHandler($type, $data = array()) {
2527 | // TODO;
2528 | }
2529 | /**
2530 | * Binds a handler to one or more events (like click) for each matched element.
2531 | * Can also bind custom events.
2532 | *
2533 | * @param unknown_type $type
2534 | * @param unknown_type $data Optional
2535 | * @param unknown_type $callback
2536 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2537 | * @TODO support '!' (exclusive) events
2538 | * @TODO support more than event in $type (space-separated)
2539 | */
2540 | public function bind($type, $data, $callback = null) {
2541 | // TODO check if $data is callable, not using is_callable
2542 | if (! isset($callback)) {
2543 | $callback = $data;
2544 | $data = null;
2545 | }
2546 | foreach($this->elements as $node)
2547 | phpQueryEvents::add($this->getDocumentID(), $node, $type, $data, $callback);
2548 | return $this;
2549 | }
2550 | /**
2551 | * Enter description here...
2552 | *
2553 | * @param unknown_type $type
2554 | * @param unknown_type $callback
2555 | * @return unknown
2556 | * @TODO namespace events
2557 | * @TODO support more than event in $type (space-separated)
2558 | */
2559 | public function unbind($type = null, $callback = null) {
2560 | foreach($this->elements as $node)
2561 | phpQueryEvents::remove($this->getDocumentID(), $node, $type, $callback);
2562 | return $this;
2563 | }
2564 | /**
2565 | * Enter description here...
2566 | *
2567 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2568 | */
2569 | public function change($callback = null) {
2570 | if ($callback)
2571 | return $this->bind('change', $callback);
2572 | return $this->trigger('change');
2573 | }
2574 | /**
2575 | * Enter description here...
2576 | *
2577 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2578 | */
2579 | public function submit($callback = null) {
2580 | if ($callback)
2581 | return $this->bind('submit', $callback);
2582 | return $this->trigger('submit');
2583 | }
2584 | /**
2585 | * Enter description here...
2586 | *
2587 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2588 | */
2589 | public function click($callback = null) {
2590 | if ($callback)
2591 | return $this->bind('click', $callback);
2592 | return $this->trigger('click');
2593 | }
2594 | /**
2595 | * Enter description here...
2596 | *
2597 | * @param String|phpQuery
2598 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2599 | */
2600 | public function wrapAllOld($wrapper) {
2601 | $wrapper = pq($wrapper)->_clone();
2602 | if (! $wrapper->length() || ! $this->length() )
2603 | return $this;
2604 | $wrapper->insertBefore($this->elements[0]);
2605 | $deepest = $wrapper->elements[0];
2606 | while($deepest->firstChild && $deepest->firstChild instanceof DOMELEMENT)
2607 | $deepest = $deepest->firstChild;
2608 | pq($deepest)->append($this);
2609 | return $this;
2610 | }
2611 | /**
2612 | * Enter description here...
2613 | *
2614 | * TODO testme...
2615 | * @param String|phpQuery
2616 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2617 | */
2618 | public function wrapAll($wrapper) {
2619 | if (! $this->length())
2620 | return $this;
2621 | return phpQuery::pq($wrapper, $this->getDocumentID())
2622 | ->clone()
2623 | ->insertBefore($this->get(0))
2624 | ->map(array($this, '___wrapAllCallback'))
2625 | ->append($this);
2626 | }
2627 | /**
2628 | *
2629 | * @param $node
2630 | * @return unknown_type
2631 | * @access private
2632 | */
2633 | public function ___wrapAllCallback($node) {
2634 | $deepest = $node;
2635 | while($deepest->firstChild && $deepest->firstChild instanceof DOMELEMENT)
2636 | $deepest = $deepest->firstChild;
2637 | return $deepest;
2638 | }
2639 | /**
2640 | * Enter description here...
2641 | * NON JQUERY METHOD
2642 | *
2643 | * @param String|phpQuery
2644 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2645 | */
2646 | public function wrapAllPHP($codeBefore, $codeAfter) {
2647 | return $this
2648 | ->slice(0, 1)
2649 | ->beforePHP($codeBefore)
2650 | ->end()
2651 | ->slice(-1)
2652 | ->afterPHP($codeAfter)
2653 | ->end();
2654 | }
2655 | /**
2656 | * Enter description here...
2657 | *
2658 | * @param String|phpQuery
2659 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2660 | */
2661 | public function wrap($wrapper) {
2662 | foreach($this->stack() as $node)
2663 | phpQuery::pq($node, $this->getDocumentID())->wrapAll($wrapper);
2664 | return $this;
2665 | }
2666 | /**
2667 | * Enter description here...
2668 | *
2669 | * @param String|phpQuery
2670 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2671 | */
2672 | public function wrapPHP($codeBefore, $codeAfter) {
2673 | foreach($this->stack() as $node)
2674 | phpQuery::pq($node, $this->getDocumentID())->wrapAllPHP($codeBefore, $codeAfter);
2675 | return $this;
2676 | }
2677 | /**
2678 | * Enter description here...
2679 | *
2680 | * @param String|phpQuery
2681 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2682 | */
2683 | public function wrapInner($wrapper) {
2684 | foreach($this->stack() as $node)
2685 | phpQuery::pq($node, $this->getDocumentID())->contents()->wrapAll($wrapper);
2686 | return $this;
2687 | }
2688 | /**
2689 | * Enter description here...
2690 | *
2691 | * @param String|phpQuery
2692 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2693 | */
2694 | public function wrapInnerPHP($codeBefore, $codeAfter) {
2695 | foreach($this->stack(1) as $node)
2696 | phpQuery::pq($node, $this->getDocumentID())->contents()
2697 | ->wrapAllPHP($codeBefore, $codeAfter);
2698 | return $this;
2699 | }
2700 | /**
2701 | * Enter description here...
2702 | *
2703 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2704 | * @testme Support for text nodes
2705 | */
2706 | public function contents() {
2707 | $stack = array();
2708 | foreach($this->stack(1) as $el) {
2709 | // FIXME (fixed) http://code.google.com/p/phpquery/issues/detail?id=56
2710 | // if (! isset($el->childNodes))
2711 | // continue;
2712 | foreach($el->childNodes as $node) {
2713 | $stack[] = $node;
2714 | }
2715 | }
2716 | return $this->newInstance($stack);
2717 | }
2718 | /**
2719 | * Enter description here...
2720 | *
2721 | * jQuery difference.
2722 | *
2723 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2724 | */
2725 | public function contentsUnwrap() {
2726 | foreach($this->stack(1) as $node) {
2727 | if (! $node->parentNode )
2728 | continue;
2729 | $childNodes = array();
2730 | // any modification in DOM tree breaks childNodes iteration, so cache them first
2731 | foreach($node->childNodes as $chNode )
2732 | $childNodes[] = $chNode;
2733 | foreach($childNodes as $chNode )
2734 | // $node->parentNode->appendChild($chNode);
2735 | $node->parentNode->insertBefore($chNode, $node);
2736 | $node->parentNode->removeChild($node);
2737 | }
2738 | return $this;
2739 | }
2740 | /**
2741 | * Enter description here...
2742 | *
2743 | * jQuery difference.
2744 | */
2745 | public function switchWith($markup) {
2746 | $markup = pq($markup, $this->getDocumentID());
2747 | $content = null;
2748 | foreach($this->stack(1) as $node) {
2749 | pq($node)
2750 | ->contents()->toReference($content)->end()
2751 | ->replaceWith($markup->clone()->append($content));
2752 | }
2753 | return $this;
2754 | }
2755 | /**
2756 | * Enter description here...
2757 | *
2758 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2759 | */
2760 | public function eq($num) {
2761 | $oldStack = $this->elements;
2762 | $this->elementsBackup = $this->elements;
2763 | $this->elements = array();
2764 | if ( isset($oldStack[$num]) )
2765 | $this->elements[] = $oldStack[$num];
2766 | return $this->newInstance();
2767 | }
2768 | /**
2769 | * Enter description here...
2770 | *
2771 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2772 | */
2773 | public function size() {
2774 | return count($this->elements);
2775 | }
2776 | /**
2777 | * Enter description here...
2778 | *
2779 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2780 | * @deprecated Use length as attribute
2781 | */
2782 | public function length() {
2783 | return $this->size();
2784 | }
2785 | public function count() {
2786 | return $this->size();
2787 | }
2788 | /**
2789 | * Enter description here...
2790 | *
2791 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2792 | * @todo $level
2793 | */
2794 | public function end($level = 1) {
2795 | // $this->elements = array_pop( $this->history );
2796 | // return $this;
2797 | // $this->previous->DOM = $this->DOM;
2798 | // $this->previous->XPath = $this->XPath;
2799 | return $this->previous
2800 | ? $this->previous
2801 | : $this;
2802 | }
2803 | /**
2804 | * Enter description here...
2805 | * Normal use ->clone() .
2806 | *
2807 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2808 | * @access private
2809 | */
2810 | public function _clone() {
2811 | $newStack = array();
2812 | //pr(array('copy... ', $this->whois()));
2813 | //$this->dumpHistory('copy');
2814 | $this->elementsBackup = $this->elements;
2815 | foreach($this->elements as $node) {
2816 | $newStack[] = $node->cloneNode(true);
2817 | }
2818 | $this->elements = $newStack;
2819 | return $this->newInstance();
2820 | }
2821 | /**
2822 | * Enter description here...
2823 | *
2824 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2825 | */
2826 | public function replaceWithPHP($code) {
2827 | return $this->replaceWith(phpQuery::php($code));
2828 | }
2829 | /**
2830 | * Enter description here...
2831 | *
2832 | * @param String|phpQuery $content
2833 | * @link http://docs.jquery.com/Manipulation/replaceWith#content
2834 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2835 | */
2836 | public function replaceWith($content) {
2837 | return $this->after($content)->remove();
2838 | }
2839 | /**
2840 | * Enter description here...
2841 | *
2842 | * @param String $selector
2843 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2844 | * @todo this works ?
2845 | */
2846 | public function replaceAll($selector) {
2847 | foreach(phpQuery::pq($selector, $this->getDocumentID()) as $node)
2848 | phpQuery::pq($node, $this->getDocumentID())
2849 | ->after($this->_clone())
2850 | ->remove();
2851 | return $this;
2852 | }
2853 | /**
2854 | * Enter description here...
2855 | *
2856 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2857 | */
2858 | public function remove($selector = null) {
2859 | $loop = $selector
2860 | ? $this->filter($selector)->elements
2861 | : $this->elements;
2862 | foreach($loop as $node) {
2863 | if (! $node->parentNode )
2864 | continue;
2865 | if (isset($node->tagName))
2866 | $this->debug("Removing '{$node->tagName}'");
2867 | $node->parentNode->removeChild($node);
2868 | // Mutation event
2869 | $event = new DOMEvent(array(
2870 | 'target' => $node,
2871 | 'type' => 'DOMNodeRemoved'
2872 | ));
2873 | phpQueryEvents::trigger($this->getDocumentID(),
2874 | $event->type, array($event), $node
2875 | );
2876 | }
2877 | return $this;
2878 | }
2879 | protected function markupEvents($newMarkup, $oldMarkup, $node) {
2880 | if ($node->tagName == 'textarea' && $newMarkup != $oldMarkup) {
2881 | $event = new DOMEvent(array(
2882 | 'target' => $node,
2883 | 'type' => 'change'
2884 | ));
2885 | phpQueryEvents::trigger($this->getDocumentID(),
2886 | $event->type, array($event), $node
2887 | );
2888 | }
2889 | }
2890 | /**
2891 | * jQuey difference
2892 | *
2893 | * @param $markup
2894 | * @return unknown_type
2895 | * @TODO trigger change event for textarea
2896 | */
2897 | public function markup($markup = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2898 | $args = func_get_args();
2899 | if ($this->documentWrapper->isXML)
2900 | return call_user_func_array(array($this, 'xml'), $args);
2901 | else
2902 | return call_user_func_array(array($this, 'html'), $args);
2903 | }
2904 | /**
2905 | * jQuey difference
2906 | *
2907 | * @param $markup
2908 | * @return unknown_type
2909 | */
2910 | public function markupOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2911 | $args = func_get_args();
2912 | if ($this->documentWrapper->isXML)
2913 | return call_user_func_array(array($this, 'xmlOuter'), $args);
2914 | else
2915 | return call_user_func_array(array($this, 'htmlOuter'), $args);
2916 | }
2917 | /**
2918 | * Enter description here...
2919 | *
2920 | * @param unknown_type $html
2921 | * @return string|phpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2922 | * @TODO force html result
2923 | */
2924 | public function html($html = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2925 | if (isset($html)) {
2926 | // INSERT
2927 | $nodes = $this->documentWrapper->import($html);
2928 | $this->empty();
2929 | foreach($this->stack(1) as $alreadyAdded => $node) {
2930 | // for now, limit events for textarea
2931 | if (($this->isXHTML() || $this->isHTML()) && $node->tagName == 'textarea')
2932 | $oldHtml = pq($node, $this->getDocumentID())->markup();
2933 | foreach($nodes as $newNode) {
2934 | $node->appendChild($alreadyAdded
2935 | ? $newNode->cloneNode(true)
2936 | : $newNode
2937 | );
2938 | }
2939 | // for now, limit events for textarea
2940 | if (($this->isXHTML() || $this->isHTML()) && $node->tagName == 'textarea')
2941 | $this->markupEvents($html, $oldHtml, $node);
2942 | }
2943 | return $this;
2944 | } else {
2945 | // FETCH
2946 | $return = $this->documentWrapper->markup($this->elements, true);
2947 | $args = func_get_args();
2948 | foreach(array_slice($args, 1) as $callback) {
2949 | $return = phpQuery::callbackRun($callback, array($return));
2950 | }
2951 | return $return;
2952 | }
2953 | }
2954 | /**
2955 | * @TODO force xml result
2956 | */
2957 | public function xml($xml = null, $callback1 = null, $callback2 = null, $callback3 = null) {
2958 | $args = func_get_args();
2959 | return call_user_func_array(array($this, 'html'), $args);
2960 | }
2961 | /**
2962 | * Enter description here...
2963 | * @TODO force html result
2964 | *
2965 | * @return String
2966 | */
2967 | public function htmlOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2968 | $markup = $this->documentWrapper->markup($this->elements);
2969 | // pass thou callbacks
2970 | $args = func_get_args();
2971 | foreach($args as $callback) {
2972 | $markup = phpQuery::callbackRun($callback, array($markup));
2973 | }
2974 | return $markup;
2975 | }
2976 | /**
2977 | * @TODO force xml result
2978 | */
2979 | public function xmlOuter($callback1 = null, $callback2 = null, $callback3 = null) {
2980 | $args = func_get_args();
2981 | return call_user_func_array(array($this, 'htmlOuter'), $args);
2982 | }
2983 | public function __toString() {
2984 | return $this->markupOuter();
2985 | }
2986 | /**
2987 | * Just like html(), but returns markup with VALID (dangerous) PHP tags.
2988 | *
2989 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
2990 | * @todo support returning markup with PHP tags when called without param
2991 | */
2992 | public function php($code = null) {
2993 | return $this->markupPHP($code);
2994 | }
2995 | /**
2996 | * Enter description here...
2997 | *
2998 | * @param $code
2999 | * @return unknown_type
3000 | */
3001 | public function markupPHP($code = null) {
3002 | return isset($code)
3003 | ? $this->markup(phpQuery::php($code))
3004 | : phpQuery::markupToPHP($this->markup());
3005 | }
3006 | /**
3007 | * Enter description here...
3008 | *
3009 | * @param $code
3010 | * @return unknown_type
3011 | */
3012 | public function markupOuterPHP() {
3013 | return phpQuery::markupToPHP($this->markupOuter());
3014 | }
3015 | /**
3016 | * Enter description here...
3017 | *
3018 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3019 | */
3020 | public function children($selector = null) {
3021 | $stack = array();
3022 | foreach($this->stack(1) as $node) {
3023 | // foreach($node->getElementsByTagName('*') as $newNode) {
3024 | foreach($node->childNodes as $newNode) {
3025 | if ($newNode->nodeType != 1)
3026 | continue;
3027 | if ($selector && ! $this->is($selector, $newNode))
3028 | continue;
3029 | if ($this->elementsContainsNode($newNode, $stack))
3030 | continue;
3031 | $stack[] = $newNode;
3032 | }
3033 | }
3034 | $this->elementsBackup = $this->elements;
3035 | $this->elements = $stack;
3036 | return $this->newInstance();
3037 | }
3038 | /**
3039 | * Enter description here...
3040 | *
3041 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3042 | */
3043 | public function ancestors($selector = null) {
3044 | return $this->children( $selector );
3045 | }
3046 | /**
3047 | * Enter description here...
3048 | *
3049 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3050 | */
3051 | public function append( $content) {
3052 | return $this->insert($content, __FUNCTION__);
3053 | }
3054 | /**
3055 | * Enter description here...
3056 | *
3057 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3058 | */
3059 | public function appendPHP( $content) {
3060 | return $this->insert("", 'append');
3061 | }
3062 | /**
3063 | * Enter description here...
3064 | *
3065 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3066 | */
3067 | public function appendTo( $seletor) {
3068 | return $this->insert($seletor, __FUNCTION__);
3069 | }
3070 | /**
3071 | * Enter description here...
3072 | *
3073 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3074 | */
3075 | public function prepend( $content) {
3076 | return $this->insert($content, __FUNCTION__);
3077 | }
3078 | /**
3079 | * Enter description here...
3080 | *
3081 | * @todo accept many arguments, which are joined, arrays maybe also
3082 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3083 | */
3084 | public function prependPHP( $content) {
3085 | return $this->insert("", 'prepend');
3086 | }
3087 | /**
3088 | * Enter description here...
3089 | *
3090 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3091 | */
3092 | public function prependTo( $seletor) {
3093 | return $this->insert($seletor, __FUNCTION__);
3094 | }
3095 | /**
3096 | * Enter description here...
3097 | *
3098 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3099 | */
3100 | public function before($content) {
3101 | return $this->insert($content, __FUNCTION__);
3102 | }
3103 | /**
3104 | * Enter description here...
3105 | *
3106 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3107 | */
3108 | public function beforePHP( $content) {
3109 | return $this->insert("", 'before');
3110 | }
3111 | /**
3112 | * Enter description here...
3113 | *
3114 | * @param String|phpQuery
3115 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3116 | */
3117 | public function insertBefore( $seletor) {
3118 | return $this->insert($seletor, __FUNCTION__);
3119 | }
3120 | /**
3121 | * Enter description here...
3122 | *
3123 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3124 | */
3125 | public function after( $content) {
3126 | return $this->insert($content, __FUNCTION__);
3127 | }
3128 | /**
3129 | * Enter description here...
3130 | *
3131 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3132 | */
3133 | public function afterPHP( $content) {
3134 | return $this->insert("", 'after');
3135 | }
3136 | /**
3137 | * Enter description here...
3138 | *
3139 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3140 | */
3141 | public function insertAfter( $seletor) {
3142 | return $this->insert($seletor, __FUNCTION__);
3143 | }
3144 | /**
3145 | * Internal insert method. Don't use it.
3146 | *
3147 | * @param unknown_type $target
3148 | * @param unknown_type $type
3149 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3150 | * @access private
3151 | */
3152 | public function insert($target, $type) {
3153 | $this->debug("Inserting data with '{$type}'");
3154 | $to = false;
3155 | switch( $type) {
3156 | case 'appendTo':
3157 | case 'prependTo':
3158 | case 'insertBefore':
3159 | case 'insertAfter':
3160 | $to = true;
3161 | }
3162 | switch(gettype($target)) {
3163 | case 'string':
3164 | $insertFrom = $insertTo = array();
3165 | if ($to) {
3166 | // INSERT TO
3167 | $insertFrom = $this->elements;
3168 | if (phpQuery::isMarkup($target)) {
3169 | // $target is new markup, import it
3170 | $insertTo = $this->documentWrapper->import($target);
3171 | // insert into selected element
3172 | } else {
3173 | // $tagret is a selector
3174 | $thisStack = $this->elements;
3175 | $this->toRoot();
3176 | $insertTo = $this->find($target)->elements;
3177 | $this->elements = $thisStack;
3178 | }
3179 | } else {
3180 | // INSERT FROM
3181 | $insertTo = $this->elements;
3182 | $insertFrom = $this->documentWrapper->import($target);
3183 | }
3184 | break;
3185 | case 'object':
3186 | $insertFrom = $insertTo = array();
3187 | // phpQuery
3188 | if ($target instanceof self) {
3189 | if ($to) {
3190 | $insertTo = $target->elements;
3191 | if ($this->documentFragment && $this->stackIsRoot())
3192 | // get all body children
3193 | // $loop = $this->find('body > *')->elements;
3194 | // TODO test it, test it hard...
3195 | // $loop = $this->newInstance($this->root)->find('> *')->elements;
3196 | $loop = $this->root->childNodes;
3197 | else
3198 | $loop = $this->elements;
3199 | // import nodes if needed
3200 | $insertFrom = $this->getDocumentID() == $target->getDocumentID()
3201 | ? $loop
3202 | : $target->documentWrapper->import($loop);
3203 | } else {
3204 | $insertTo = $this->elements;
3205 | if ( $target->documentFragment && $target->stackIsRoot() )
3206 | // get all body children
3207 | // $loop = $target->find('body > *')->elements;
3208 | $loop = $target->root->childNodes;
3209 | else
3210 | $loop = $target->elements;
3211 | // import nodes if needed
3212 | $insertFrom = $this->getDocumentID() == $target->getDocumentID()
3213 | ? $loop
3214 | : $this->documentWrapper->import($loop);
3215 | }
3216 | // DOMNODE
3217 | } elseif ($target instanceof DOMNODE) {
3218 | // import node if needed
3219 | // if ( $target->ownerDocument != $this->DOM )
3220 | // $target = $this->DOM->importNode($target, true);
3221 | if ( $to) {
3222 | $insertTo = array($target);
3223 | if ($this->documentFragment && $this->stackIsRoot())
3224 | // get all body children
3225 | $loop = $this->root->childNodes;
3226 | // $loop = $this->find('body > *')->elements;
3227 | else
3228 | $loop = $this->elements;
3229 | foreach($loop as $fromNode)
3230 | // import nodes if needed
3231 | $insertFrom[] = ! $fromNode->ownerDocument->isSameNode($target->ownerDocument)
3232 | ? $target->ownerDocument->importNode($fromNode, true)
3233 | : $fromNode;
3234 | } else {
3235 | // import node if needed
3236 | if (! $target->ownerDocument->isSameNode($this->document))
3237 | $target = $this->document->importNode($target, true);
3238 | $insertTo = $this->elements;
3239 | $insertFrom[] = $target;
3240 | }
3241 | }
3242 | break;
3243 | }
3244 | phpQuery::debug("From ".count($insertFrom)."; To ".count($insertTo)." nodes");
3245 | foreach($insertTo as $insertNumber => $toNode) {
3246 | // we need static relative elements in some cases
3247 | switch( $type) {
3248 | case 'prependTo':
3249 | case 'prepend':
3250 | $firstChild = $toNode->firstChild;
3251 | break;
3252 | case 'insertAfter':
3253 | case 'after':
3254 | $nextSibling = $toNode->nextSibling;
3255 | break;
3256 | }
3257 | foreach($insertFrom as $fromNode) {
3258 | // clone if inserted already before
3259 | $insert = $insertNumber
3260 | ? $fromNode->cloneNode(true)
3261 | : $fromNode;
3262 | switch($type) {
3263 | case 'appendTo':
3264 | case 'append':
3265 | // $toNode->insertBefore(
3266 | // $fromNode,
3267 | // $toNode->lastChild->nextSibling
3268 | // );
3269 | $toNode->appendChild($insert);
3270 | $eventTarget = $insert;
3271 | break;
3272 | case 'prependTo':
3273 | case 'prepend':
3274 | $toNode->insertBefore(
3275 | $insert,
3276 | $firstChild
3277 | );
3278 | break;
3279 | case 'insertBefore':
3280 | case 'before':
3281 | if (! $toNode->parentNode)
3282 | throw new Exception("No parentNode, can't do {$type}()");
3283 | else
3284 | $toNode->parentNode->insertBefore(
3285 | $insert,
3286 | $toNode
3287 | );
3288 | break;
3289 | case 'insertAfter':
3290 | case 'after':
3291 | if (! $toNode->parentNode)
3292 | throw new Exception("No parentNode, can't do {$type}()");
3293 | else
3294 | $toNode->parentNode->insertBefore(
3295 | $insert,
3296 | $nextSibling
3297 | );
3298 | break;
3299 | }
3300 | // Mutation event
3301 | $event = new DOMEvent(array(
3302 | 'target' => $insert,
3303 | 'type' => 'DOMNodeInserted'
3304 | ));
3305 | phpQueryEvents::trigger($this->getDocumentID(),
3306 | $event->type, array($event), $insert
3307 | );
3308 | }
3309 | }
3310 | return $this;
3311 | }
3312 | /**
3313 | * Enter description here...
3314 | *
3315 | * @return Int
3316 | */
3317 | public function index($subject) {
3318 | $index = -1;
3319 | $subject = $subject instanceof phpQueryObject
3320 | ? $subject->elements[0]
3321 | : $subject;
3322 | foreach($this->newInstance() as $k => $node) {
3323 | if ($node->isSameNode($subject))
3324 | $index = $k;
3325 | }
3326 | return $index;
3327 | }
3328 | /**
3329 | * Enter description here...
3330 | *
3331 | * @param unknown_type $start
3332 | * @param unknown_type $end
3333 | *
3334 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3335 | * @testme
3336 | */
3337 | public function slice($start, $end = null) {
3338 | // $last = count($this->elements)-1;
3339 | // $end = $end
3340 | // ? min($end, $last)
3341 | // : $last;
3342 | // if ($start < 0)
3343 | // $start = $last+$start;
3344 | // if ($start > $last)
3345 | // return array();
3346 | if ($end > 0)
3347 | $end = $end-$start;
3348 | return $this->newInstance(
3349 | array_slice($this->elements, $start, $end)
3350 | );
3351 | }
3352 | /**
3353 | * Enter description here...
3354 | *
3355 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3356 | */
3357 | public function reverse() {
3358 | $this->elementsBackup = $this->elements;
3359 | $this->elements = array_reverse($this->elements);
3360 | return $this->newInstance();
3361 | }
3362 | /**
3363 | * Return joined text content.
3364 | * @return String
3365 | */
3366 | public function text($text = null, $callback1 = null, $callback2 = null, $callback3 = null) {
3367 | if (isset($text))
3368 | return $this->html(htmlspecialchars($text));
3369 | $args = func_get_args();
3370 | $args = array_slice($args, 1);
3371 | $return = '';
3372 | foreach($this->elements as $node) {
3373 | $text = $node->textContent;
3374 | if (count($this->elements) > 1 && $text)
3375 | $text .= "\n";
3376 | foreach($args as $callback) {
3377 | $text = phpQuery::callbackRun($callback, array($text));
3378 | }
3379 | $return .= $text;
3380 | }
3381 | return $return;
3382 | }
3383 | /**
3384 | * Enter description here...
3385 | *
3386 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3387 | */
3388 | public function plugin($class, $file = null) {
3389 | phpQuery::plugin($class, $file);
3390 | return $this;
3391 | }
3392 | /**
3393 | * Deprecated, use $pq->plugin() instead.
3394 | *
3395 | * @deprecated
3396 | * @param $class
3397 | * @param $file
3398 | * @return unknown_type
3399 | */
3400 | public static function extend($class, $file = null) {
3401 | return $this->plugin($class, $file);
3402 | }
3403 | /**
3404 | *
3405 | * @access private
3406 | * @param $method
3407 | * @param $args
3408 | * @return unknown_type
3409 | */
3410 | public function __call($method, $args) {
3411 | $aliasMethods = array('clone', 'empty');
3412 | if (isset(phpQuery::$extendMethods[$method])) {
3413 | array_unshift($args, $this);
3414 | return phpQuery::callbackRun(
3415 | phpQuery::$extendMethods[$method], $args
3416 | );
3417 | } else if (isset(phpQuery::$pluginsMethods[$method])) {
3418 | array_unshift($args, $this);
3419 | $class = phpQuery::$pluginsMethods[$method];
3420 | $realClass = "phpQueryObjectPlugin_$class";
3421 | $return = call_user_func_array(
3422 | array($realClass, $method),
3423 | $args
3424 | );
3425 | // XXX deprecate ?
3426 | return is_null($return)
3427 | ? $this
3428 | : $return;
3429 | } else if (in_array($method, $aliasMethods)) {
3430 | return call_user_func_array(array($this, '_'.$method), $args);
3431 | } else
3432 | throw new Exception("Method '{$method}' doesnt exist");
3433 | }
3434 | /**
3435 | * Safe rename of next().
3436 | *
3437 | * Use it ONLY when need to call next() on an iterated object (in same time).
3438 | * Normaly there is no need to do such thing ;)
3439 | *
3440 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3441 | * @access private
3442 | */
3443 | public function _next($selector = null) {
3444 | return $this->newInstance(
3445 | $this->getElementSiblings('nextSibling', $selector, true)
3446 | );
3447 | }
3448 | /**
3449 | * Use prev() and next().
3450 | *
3451 | * @deprecated
3452 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3453 | * @access private
3454 | */
3455 | public function _prev($selector = null) {
3456 | return $this->prev($selector);
3457 | }
3458 | /**
3459 | * Enter description here...
3460 | *
3461 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3462 | */
3463 | public function prev($selector = null) {
3464 | return $this->newInstance(
3465 | $this->getElementSiblings('previousSibling', $selector, true)
3466 | );
3467 | }
3468 | /**
3469 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3470 | * @todo
3471 | */
3472 | public function prevAll($selector = null) {
3473 | return $this->newInstance(
3474 | $this->getElementSiblings('previousSibling', $selector)
3475 | );
3476 | }
3477 | /**
3478 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3479 | * @todo FIXME: returns source elements insted of next siblings
3480 | */
3481 | public function nextAll($selector = null) {
3482 | return $this->newInstance(
3483 | $this->getElementSiblings('nextSibling', $selector)
3484 | );
3485 | }
3486 | /**
3487 | * @access private
3488 | */
3489 | protected function getElementSiblings($direction, $selector = null, $limitToOne = false) {
3490 | $stack = array();
3491 | $count = 0;
3492 | foreach($this->stack() as $node) {
3493 | $test = $node;
3494 | while( isset($test->{$direction}) && $test->{$direction}) {
3495 | $test = $test->{$direction};
3496 | if (! $test instanceof DOMELEMENT)
3497 | continue;
3498 | $stack[] = $test;
3499 | if ($limitToOne)
3500 | break;
3501 | }
3502 | }
3503 | if ($selector) {
3504 | $stackOld = $this->elements;
3505 | $this->elements = $stack;
3506 | $stack = $this->filter($selector, true)->stack();
3507 | $this->elements = $stackOld;
3508 | }
3509 | return $stack;
3510 | }
3511 | /**
3512 | * Enter description here...
3513 | *
3514 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3515 | */
3516 | public function siblings($selector = null) {
3517 | $stack = array();
3518 | $siblings = array_merge(
3519 | $this->getElementSiblings('previousSibling', $selector),
3520 | $this->getElementSiblings('nextSibling', $selector)
3521 | );
3522 | foreach($siblings as $node) {
3523 | if (! $this->elementsContainsNode($node, $stack))
3524 | $stack[] = $node;
3525 | }
3526 | return $this->newInstance($stack);
3527 | }
3528 | /**
3529 | * Enter description here...
3530 | *
3531 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3532 | */
3533 | public function not($selector = null) {
3534 | if (is_string($selector))
3535 | phpQuery::debug(array('not', $selector));
3536 | else
3537 | phpQuery::debug('not');
3538 | $stack = array();
3539 | if ($selector instanceof self || $selector instanceof DOMNODE) {
3540 | foreach($this->stack() as $node) {
3541 | if ($selector instanceof self) {
3542 | $matchFound = false;
3543 | foreach($selector->stack() as $notNode) {
3544 | if ($notNode->isSameNode($node))
3545 | $matchFound = true;
3546 | }
3547 | if (! $matchFound)
3548 | $stack[] = $node;
3549 | } else if ($selector instanceof DOMNODE) {
3550 | if (! $selector->isSameNode($node))
3551 | $stack[] = $node;
3552 | } else {
3553 | if (! $this->is($selector))
3554 | $stack[] = $node;
3555 | }
3556 | }
3557 | } else {
3558 | $orgStack = $this->stack();
3559 | $matched = $this->filter($selector, true)->stack();
3560 | // $matched = array();
3561 | // // simulate OR in filter() instead of AND 5y
3562 | // foreach($this->parseSelector($selector) as $s) {
3563 | // $matched = array_merge($matched,
3564 | // $this->filter(array($s))->stack()
3565 | // );
3566 | // }
3567 | foreach($orgStack as $node)
3568 | if (! $this->elementsContainsNode($node, $matched))
3569 | $stack[] = $node;
3570 | }
3571 | return $this->newInstance($stack);
3572 | }
3573 | /**
3574 | * Enter description here...
3575 | *
3576 | * @param string|phpQueryObject
3577 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3578 | */
3579 | public function add($selector = null) {
3580 | if (! $selector)
3581 | return $this;
3582 | $stack = array();
3583 | $this->elementsBackup = $this->elements;
3584 | $found = phpQuery::pq($selector, $this->getDocumentID());
3585 | $this->merge($found->elements);
3586 | return $this->newInstance();
3587 | }
3588 | /**
3589 | * @access private
3590 | */
3591 | protected function merge() {
3592 | foreach(func_get_args() as $nodes)
3593 | foreach($nodes as $newNode )
3594 | if (! $this->elementsContainsNode($newNode) )
3595 | $this->elements[] = $newNode;
3596 | }
3597 | /**
3598 | * @access private
3599 | * TODO refactor to stackContainsNode
3600 | */
3601 | protected function elementsContainsNode($nodeToCheck, $elementsStack = null) {
3602 | $loop = ! is_null($elementsStack)
3603 | ? $elementsStack
3604 | : $this->elements;
3605 | foreach($loop as $node) {
3606 | if ( $node->isSameNode( $nodeToCheck ) )
3607 | return true;
3608 | }
3609 | return false;
3610 | }
3611 | /**
3612 | * Enter description here...
3613 | *
3614 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3615 | */
3616 | public function parent($selector = null) {
3617 | $stack = array();
3618 | foreach($this->elements as $node )
3619 | if ( $node->parentNode && ! $this->elementsContainsNode($node->parentNode, $stack) )
3620 | $stack[] = $node->parentNode;
3621 | $this->elementsBackup = $this->elements;
3622 | $this->elements = $stack;
3623 | if ( $selector )
3624 | $this->filter($selector, true);
3625 | return $this->newInstance();
3626 | }
3627 | /**
3628 | * Enter description here...
3629 | *
3630 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3631 | */
3632 | public function parents($selector = null) {
3633 | $stack = array();
3634 | if (! $this->elements )
3635 | $this->debug('parents() - stack empty');
3636 | foreach($this->elements as $node) {
3637 | $test = $node;
3638 | while( $test->parentNode) {
3639 | $test = $test->parentNode;
3640 | if ($this->isRoot($test))
3641 | break;
3642 | if (! $this->elementsContainsNode($test, $stack)) {
3643 | $stack[] = $test;
3644 | continue;
3645 | }
3646 | }
3647 | }
3648 | $this->elementsBackup = $this->elements;
3649 | $this->elements = $stack;
3650 | if ( $selector )
3651 | $this->filter($selector, true);
3652 | return $this->newInstance();
3653 | }
3654 | /**
3655 | * Internal stack iterator.
3656 | *
3657 | * @access private
3658 | */
3659 | public function stack($nodeTypes = null) {
3660 | if (!isset($nodeTypes))
3661 | return $this->elements;
3662 | if (!is_array($nodeTypes))
3663 | $nodeTypes = array($nodeTypes);
3664 | $return = array();
3665 | foreach($this->elements as $node) {
3666 | if (in_array($node->nodeType, $nodeTypes))
3667 | $return[] = $node;
3668 | }
3669 | return $return;
3670 | }
3671 | // TODO phpdoc; $oldAttr is result of hasAttribute, before any changes
3672 | protected function attrEvents($attr, $oldAttr, $oldValue, $node) {
3673 | // skip events for XML documents
3674 | if (! $this->isXHTML() && ! $this->isHTML())
3675 | return;
3676 | $event = null;
3677 | // identify
3678 | $isInputValue = $node->tagName == 'input'
3679 | && (
3680 | in_array($node->getAttribute('type'),
3681 | array('text', 'password', 'hidden'))
3682 | || !$node->getAttribute('type')
3683 | );
3684 | $isRadio = $node->tagName == 'input'
3685 | && $node->getAttribute('type') == 'radio';
3686 | $isCheckbox = $node->tagName == 'input'
3687 | && $node->getAttribute('type') == 'checkbox';
3688 | $isOption = $node->tagName == 'option';
3689 | if ($isInputValue && $attr == 'value' && $oldValue != $node->getAttribute($attr)) {
3690 | $event = new DOMEvent(array(
3691 | 'target' => $node,
3692 | 'type' => 'change'
3693 | ));
3694 | } else if (($isRadio || $isCheckbox) && $attr == 'checked' && (
3695 | // check
3696 | (! $oldAttr && $node->hasAttribute($attr))
3697 | // un-check
3698 | || (! $node->hasAttribute($attr) && $oldAttr)
3699 | )) {
3700 | $event = new DOMEvent(array(
3701 | 'target' => $node,
3702 | 'type' => 'change'
3703 | ));
3704 | } else if ($isOption && $node->parentNode && $attr == 'selected' && (
3705 | // select
3706 | (! $oldAttr && $node->hasAttribute($attr))
3707 | // un-select
3708 | || (! $node->hasAttribute($attr) && $oldAttr)
3709 | )) {
3710 | $event = new DOMEvent(array(
3711 | 'target' => $node->parentNode,
3712 | 'type' => 'change'
3713 | ));
3714 | }
3715 | if ($event) {
3716 | phpQueryEvents::trigger($this->getDocumentID(),
3717 | $event->type, array($event), $node
3718 | );
3719 | }
3720 | }
3721 | public function attr($attr = null, $value = null) {
3722 | foreach($this->stack(1) as $node) {
3723 | if (! is_null($value)) {
3724 | $loop = $attr == '*'
3725 | ? $this->getNodeAttrs($node)
3726 | : array($attr);
3727 | foreach($loop as $a) {
3728 | $oldValue = $node->getAttribute($a);
3729 | $oldAttr = $node->hasAttribute($a);
3730 | // TODO raises an error when charset other than UTF-8
3731 | // while document's charset is also not UTF-8
3732 | @$node->setAttribute($a, $value);
3733 | $this->attrEvents($a, $oldAttr, $oldValue, $node);
3734 | }
3735 | } else if ($attr == '*') {
3736 | // jQuery difference
3737 | $return = array();
3738 | foreach($node->attributes as $n => $v)
3739 | $return[$n] = $v->value;
3740 | return $return;
3741 | } else
3742 | return $node->hasAttribute($attr)
3743 | ? $node->getAttribute($attr)
3744 | : null;
3745 | }
3746 | return is_null($value)
3747 | ? '' : $this;
3748 | }
3749 | /**
3750 | * @access private
3751 | */
3752 | protected function getNodeAttrs($node) {
3753 | $return = array();
3754 | foreach($node->attributes as $n => $o)
3755 | $return[] = $n;
3756 | return $return;
3757 | }
3758 | /**
3759 | * Enter description here...
3760 | *
3761 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3762 | * @todo check CDATA ???
3763 | */
3764 | public function attrPHP($attr, $code) {
3765 | if (! is_null($code)) {
3766 | $value = '<'.'?php '.$code.' ?'.'>';
3767 | // TODO tempolary solution
3768 | // http://code.google.com/p/phpquery/issues/detail?id=17
3769 | // if (function_exists('mb_detect_encoding') && mb_detect_encoding($value) == 'ASCII')
3770 | // $value = mb_convert_encoding($value, 'UTF-8', 'HTML-ENTITIES');
3771 | }
3772 | foreach($this->stack(1) as $node) {
3773 | if (! is_null($code)) {
3774 | // $attrNode = $this->DOM->createAttribute($attr);
3775 | $node->setAttribute($attr, $value);
3776 | // $attrNode->value = $value;
3777 | // $node->appendChild($attrNode);
3778 | } else if ( $attr == '*') {
3779 | // jQuery diff
3780 | $return = array();
3781 | foreach($node->attributes as $n => $v)
3782 | $return[$n] = $v->value;
3783 | return $return;
3784 | } else
3785 | return $node->getAttribute($attr);
3786 | }
3787 | return $this;
3788 | }
3789 | /**
3790 | * Enter description here...
3791 | *
3792 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3793 | */
3794 | public function removeAttr($attr) {
3795 | foreach($this->stack(1) as $node) {
3796 | $loop = $attr == '*'
3797 | ? $this->getNodeAttrs($node)
3798 | : array($attr);
3799 | foreach($loop as $a) {
3800 | $oldValue = $node->getAttribute($a);
3801 | $node->removeAttribute($a);
3802 | $this->attrEvents($a, $oldValue, null, $node);
3803 | }
3804 | }
3805 | return $this;
3806 | }
3807 | /**
3808 | * Return form element value.
3809 | *
3810 | * @return String Fields value.
3811 | */
3812 | public function val($val = null) {
3813 | if (! isset($val)) {
3814 | if ($this->eq(0)->is('select')) {
3815 | $selected = $this->eq(0)->find('option[selected=selected]');
3816 | if ($selected->is('[value]'))
3817 | return $selected->attr('value');
3818 | else
3819 | return $selected->text();
3820 | } else if ($this->eq(0)->is('textarea'))
3821 | return $this->eq(0)->markup();
3822 | else
3823 | return $this->eq(0)->attr('value');
3824 | } else {
3825 | $_val = null;
3826 | foreach($this->stack(1) as $node) {
3827 | $node = pq($node, $this->getDocumentID());
3828 | if (is_array($val) && in_array($node->attr('type'), array('checkbox', 'radio'))) {
3829 | $isChecked = in_array($node->attr('value'), $val)
3830 | || in_array($node->attr('name'), $val);
3831 | if ($isChecked)
3832 | $node->attr('checked', 'checked');
3833 | else
3834 | $node->removeAttr('checked');
3835 | } else if ($node->get(0)->tagName == 'select') {
3836 | if (! isset($_val)) {
3837 | $_val = array();
3838 | if (! is_array($val))
3839 | $_val = array((string)$val);
3840 | else
3841 | foreach($val as $v)
3842 | $_val[] = $v;
3843 | }
3844 | foreach($node['option']->stack(1) as $option) {
3845 | $option = pq($option, $this->getDocumentID());
3846 | $selected = false;
3847 | // XXX: workaround for string comparsion, see issue #96
3848 | // http://code.google.com/p/phpquery/issues/detail?id=96
3849 | $selected = is_null($option->attr('value'))
3850 | ? in_array($option->markup(), $_val)
3851 | : in_array($option->attr('value'), $_val);
3852 | // $optionValue = $option->attr('value');
3853 | // $optionText = $option->text();
3854 | // $optionTextLenght = mb_strlen($optionText);
3855 | // foreach($_val as $v)
3856 | // if ($optionValue == $v)
3857 | // $selected = true;
3858 | // else if ($optionText == $v && $optionTextLenght == mb_strlen($v))
3859 | // $selected = true;
3860 | if ($selected)
3861 | $option->attr('selected', 'selected');
3862 | else
3863 | $option->removeAttr('selected');
3864 | }
3865 | } else if ($node->get(0)->tagName == 'textarea')
3866 | $node->markup($val);
3867 | else
3868 | $node->attr('value', $val);
3869 | }
3870 | }
3871 | return $this;
3872 | }
3873 | /**
3874 | * Enter description here...
3875 | *
3876 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3877 | */
3878 | public function andSelf() {
3879 | if ( $this->previous )
3880 | $this->elements = array_merge($this->elements, $this->previous->elements);
3881 | return $this;
3882 | }
3883 | /**
3884 | * Enter description here...
3885 | *
3886 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3887 | */
3888 | public function addClass( $className) {
3889 | if (! $className)
3890 | return $this;
3891 | foreach($this->stack(1) as $node) {
3892 | if (! $this->is(".$className", $node))
3893 | $node->setAttribute(
3894 | 'class',
3895 | trim($node->getAttribute('class').' '.$className)
3896 | );
3897 | }
3898 | return $this;
3899 | }
3900 | /**
3901 | * Enter description here...
3902 | *
3903 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3904 | */
3905 | public function addClassPHP( $className) {
3906 | foreach($this->stack(1) as $node) {
3907 | $classes = $node->getAttribute('class');
3908 | $newValue = $classes
3909 | ? $classes.' <'.'?php '.$className.' ?'.'>'
3910 | : '<'.'?php '.$className.' ?'.'>';
3911 | $node->setAttribute('class', $newValue);
3912 | }
3913 | return $this;
3914 | }
3915 | /**
3916 | * Enter description here...
3917 | *
3918 | * @param string $className
3919 | * @return bool
3920 | */
3921 | public function hasClass($className) {
3922 | foreach($this->stack(1) as $node) {
3923 | if ( $this->is(".$className", $node))
3924 | return true;
3925 | }
3926 | return false;
3927 | }
3928 | /**
3929 | * Enter description here...
3930 | *
3931 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3932 | */
3933 | public function removeClass($className) {
3934 | foreach($this->stack(1) as $node) {
3935 | $classes = explode( ' ', $node->getAttribute('class'));
3936 | if ( in_array($className, $classes)) {
3937 | $classes = array_diff($classes, array($className));
3938 | if ( $classes )
3939 | $node->setAttribute('class', implode(' ', $classes));
3940 | else
3941 | $node->removeAttribute('class');
3942 | }
3943 | }
3944 | return $this;
3945 | }
3946 | /**
3947 | * Enter description here...
3948 | *
3949 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3950 | */
3951 | public function toggleClass($className) {
3952 | foreach($this->stack(1) as $node) {
3953 | if ( $this->is( $node, '.'.$className ))
3954 | $this->removeClass($className);
3955 | else
3956 | $this->addClass($className);
3957 | }
3958 | return $this;
3959 | }
3960 | /**
3961 | * Proper name without underscore (just ->empty()) also works.
3962 | *
3963 | * Removes all child nodes from the set of matched elements.
3964 | *
3965 | * Example:
3966 | * pq("p")._empty()
3967 | *
3968 | * HTML:
3969 | * Hello, Person and person
3970 | *
3971 | * Result:
3972 | * [ ]
3973 | *
3974 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3975 | * @access private
3976 | */
3977 | public function _empty() {
3978 | foreach($this->stack(1) as $node) {
3979 | // thx to 'dave at dgx dot cz'
3980 | $node->nodeValue = '';
3981 | }
3982 | return $this;
3983 | }
3984 | /**
3985 | * Enter description here...
3986 | *
3987 | * @param array|string $callback Expects $node as first param, $index as second
3988 | * @param array $scope External variables passed to callback. Use compact('varName1', 'varName2'...) and extract($scope)
3989 | * @param array $arg1 Will ba passed as third and futher args to callback.
3990 | * @param array $arg2 Will ba passed as fourth and futher args to callback, and so on...
3991 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
3992 | */
3993 | public function each($callback, $param1 = null, $param2 = null, $param3 = null) {
3994 | $paramStructure = null;
3995 | if (func_num_args() > 1) {
3996 | $paramStructure = func_get_args();
3997 | $paramStructure = array_slice($paramStructure, 1);
3998 | }
3999 | foreach($this->elements as $v)
4000 | phpQuery::callbackRun($callback, array($v), $paramStructure);
4001 | return $this;
4002 | }
4003 | /**
4004 | * Run callback on actual object.
4005 | *
4006 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4007 | */
4008 | public function callback($callback, $param1 = null, $param2 = null, $param3 = null) {
4009 | $params = func_get_args();
4010 | $params[0] = $this;
4011 | phpQuery::callbackRun($callback, $params);
4012 | return $this;
4013 | }
4014 | /**
4015 | * Enter description here...
4016 | *
4017 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4018 | * @todo add $scope and $args as in each() ???
4019 | */
4020 | public function map($callback, $param1 = null, $param2 = null, $param3 = null) {
4021 | // $stack = array();
4022 | //// foreach($this->newInstance() as $node) {
4023 | // foreach($this->newInstance() as $node) {
4024 | // $result = call_user_func($callback, $node);
4025 | // if ($result)
4026 | // $stack[] = $result;
4027 | // }
4028 | $params = func_get_args();
4029 | array_unshift($params, $this->elements);
4030 | return $this->newInstance(
4031 | call_user_func_array(array('phpQuery', 'map'), $params)
4032 | // phpQuery::map($this->elements, $callback)
4033 | );
4034 | }
4035 | /**
4036 | * Enter description here...
4037 | *
4038 | * @param $key
4039 | * @param $value
4040 | */
4041 | public function data($key, $value = null) {
4042 | if (! isset($value)) {
4043 | // TODO? implement specific jQuery behavior od returning parent values
4044 | // is child which we look up doesn't exist
4045 | return phpQuery::data($this->get(0), $key, $value, $this->getDocumentID());
4046 | } else {
4047 | foreach($this as $node)
4048 | phpQuery::data($node, $key, $value, $this->getDocumentID());
4049 | return $this;
4050 | }
4051 | }
4052 | /**
4053 | * Enter description here...
4054 | *
4055 | * @param $key
4056 | */
4057 | public function removeData($key) {
4058 | foreach($this as $node)
4059 | phpQuery::removeData($node, $key, $this->getDocumentID());
4060 | return $this;
4061 | }
4062 | // INTERFACE IMPLEMENTATIONS
4063 |
4064 | // ITERATOR INTERFACE
4065 | /**
4066 | * @access private
4067 | */
4068 | public function rewind(){
4069 | $this->debug('iterating foreach');
4070 | // phpQuery::selectDocument($this->getDocumentID());
4071 | $this->elementsBackup = $this->elements;
4072 | $this->elementsInterator = $this->elements;
4073 | $this->valid = isset( $this->elements[0] )
4074 | ? 1 : 0;
4075 | // $this->elements = $this->valid
4076 | // ? array($this->elements[0])
4077 | // : array();
4078 | $this->current = 0;
4079 | }
4080 | /**
4081 | * @access private
4082 | */
4083 | public function current(){
4084 | return $this->elementsInterator[ $this->current ];
4085 | }
4086 | /**
4087 | * @access private
4088 | */
4089 | public function key(){
4090 | return $this->current;
4091 | }
4092 | /**
4093 | * Double-function method.
4094 | *
4095 | * First: main iterator interface method.
4096 | * Second: Returning next sibling, alias for _next().
4097 | *
4098 | * Proper functionality is choosed automagicaly.
4099 | *
4100 | * @see phpQueryObject::_next()
4101 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4102 | */
4103 | public function next($cssSelector = null){
4104 | // if ($cssSelector || $this->valid)
4105 | // return $this->_next($cssSelector);
4106 | $this->valid = isset( $this->elementsInterator[ $this->current+1 ] )
4107 | ? true
4108 | : false;
4109 | if (! $this->valid && $this->elementsInterator) {
4110 | $this->elementsInterator = null;
4111 | } else if ($this->valid) {
4112 | $this->current++;
4113 | } else {
4114 | return $this->_next($cssSelector);
4115 | }
4116 | }
4117 | /**
4118 | * @access private
4119 | */
4120 | public function valid(){
4121 | return $this->valid;
4122 | }
4123 | // ITERATOR INTERFACE END
4124 | // ARRAYACCESS INTERFACE
4125 | /**
4126 | * @access private
4127 | */
4128 | public function offsetExists($offset) {
4129 | return $this->find($offset)->size() > 0;
4130 | }
4131 | /**
4132 | * @access private
4133 | */
4134 | public function offsetGet($offset) {
4135 | return $this->find($offset);
4136 | }
4137 | /**
4138 | * @access private
4139 | */
4140 | public function offsetSet($offset, $value) {
4141 | // $this->find($offset)->replaceWith($value);
4142 | $this->find($offset)->html($value);
4143 | }
4144 | /**
4145 | * @access private
4146 | */
4147 | public function offsetUnset($offset) {
4148 | // empty
4149 | throw new Exception("Can't do unset, use array interface only for calling queries and replacing HTML.");
4150 | }
4151 | // ARRAYACCESS INTERFACE END
4152 | /**
4153 | * Returns node's XPath.
4154 | *
4155 | * @param unknown_type $oneNode
4156 | * @return string
4157 | * @TODO use native getNodePath is avaible
4158 | * @access private
4159 | */
4160 | protected function getNodeXpath($oneNode = null, $namespace = null) {
4161 | $return = array();
4162 | $loop = $oneNode
4163 | ? array($oneNode)
4164 | : $this->elements;
4165 | // if ($namespace)
4166 | // $namespace .= ':';
4167 | foreach($loop as $node) {
4168 | if ($node instanceof DOMDOCUMENT) {
4169 | $return[] = '';
4170 | continue;
4171 | }
4172 | $xpath = array();
4173 | while(! ($node instanceof DOMDOCUMENT)) {
4174 | $i = 1;
4175 | $sibling = $node;
4176 | while($sibling->previousSibling) {
4177 | $sibling = $sibling->previousSibling;
4178 | $isElement = $sibling instanceof DOMELEMENT;
4179 | if ($isElement && $sibling->tagName == $node->tagName)
4180 | $i++;
4181 | }
4182 | $xpath[] = $this->isXML()
4183 | ? "*[local-name()='{$node->tagName}'][{$i}]"
4184 | : "{$node->tagName}[{$i}]";
4185 | $node = $node->parentNode;
4186 | }
4187 | $xpath = join('/', array_reverse($xpath));
4188 | $return[] = '/'.$xpath;
4189 | }
4190 | return $oneNode
4191 | ? $return[0]
4192 | : $return;
4193 | }
4194 | // HELPERS
4195 | public function whois($oneNode = null) {
4196 | $return = array();
4197 | $loop = $oneNode
4198 | ? array( $oneNode )
4199 | : $this->elements;
4200 | foreach($loop as $node) {
4201 | if (isset($node->tagName)) {
4202 | $tag = in_array($node->tagName, array('php', 'js'))
4203 | ? strtoupper($node->tagName)
4204 | : $node->tagName;
4205 | $return[] = $tag
4206 | .($node->getAttribute('id')
4207 | ? '#'.$node->getAttribute('id'):'')
4208 | .($node->getAttribute('class')
4209 | ? '.'.join('.', split(' ', $node->getAttribute('class'))):'')
4210 | .($node->getAttribute('name')
4211 | ? '[name="'.$node->getAttribute('name').'"]':'')
4212 | .($node->getAttribute('value') && strpos($node->getAttribute('value'), '<'.'?php') === false
4213 | ? '[value="'.substr(str_replace("\n", '', $node->getAttribute('value')), 0, 15).'"]':'')
4214 | .($node->getAttribute('value') && strpos($node->getAttribute('value'), '<'.'?php') !== false
4215 | ? '[value=PHP]':'')
4216 | .($node->getAttribute('selected')
4217 | ? '[selected]':'')
4218 | .($node->getAttribute('checked')
4219 | ? '[checked]':'')
4220 | ;
4221 | } else if ($node instanceof DOMTEXT) {
4222 | if (trim($node->textContent))
4223 | $return[] = 'Text:'.substr(str_replace("\n", ' ', $node->textContent), 0, 15);
4224 | } else {
4225 |
4226 | }
4227 | }
4228 | return $oneNode && isset($return[0])
4229 | ? $return[0]
4230 | : $return;
4231 | }
4232 | /**
4233 | * Dump htmlOuter and preserve chain. Usefull for debugging.
4234 | *
4235 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4236 | *
4237 | */
4238 | public function dump() {
4239 | print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4240 | $debug = phpQuery::$debug;
4241 | phpQuery::$debug = false;
4242 | // print __FILE__.':'.__LINE__."\n";
4243 | var_dump($this->htmlOuter());
4244 | return $this;
4245 | }
4246 | public function dumpWhois() {
4247 | print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4248 | $debug = phpQuery::$debug;
4249 | phpQuery::$debug = false;
4250 | // print __FILE__.':'.__LINE__."\n";
4251 | var_dump('whois', $this->whois());
4252 | phpQuery::$debug = $debug;
4253 | return $this;
4254 | }
4255 | public function dumpLength() {
4256 | print 'DUMP #'.(phpQuery::$dumpCount++).' ';
4257 | $debug = phpQuery::$debug;
4258 | phpQuery::$debug = false;
4259 | // print __FILE__.':'.__LINE__."\n";
4260 | var_dump('length', $this->length());
4261 | phpQuery::$debug = $debug;
4262 | return $this;
4263 | }
4264 | public function dumpTree($html = true, $title = true) {
4265 | $output = $title
4266 | ? 'DUMP #'.(phpQuery::$dumpCount++)." \n" : '';
4267 | $debug = phpQuery::$debug;
4268 | phpQuery::$debug = false;
4269 | foreach($this->stack() as $node)
4270 | $output .= $this->__dumpTree($node);
4271 | phpQuery::$debug = $debug;
4272 | print $html
4273 | ? nl2br(str_replace(' ', ' ', $output))
4274 | : $output;
4275 | return $this;
4276 | }
4277 | private function __dumpTree($node, $intend = 0) {
4278 | $whois = $this->whois($node);
4279 | $return = '';
4280 | if ($whois)
4281 | $return .= str_repeat(' - ', $intend).$whois."\n";
4282 | if (isset($node->childNodes))
4283 | foreach($node->childNodes as $chNode)
4284 | $return .= $this->__dumpTree($chNode, $intend+1);
4285 | return $return;
4286 | }
4287 | /**
4288 | * Dump htmlOuter and stop script execution. Usefull for debugging.
4289 | *
4290 | */
4291 | public function dumpDie() {
4292 | print __FILE__.':'.__LINE__;
4293 | var_dump($this->htmlOuter());
4294 | die();
4295 | }
4296 | }
4297 |
4298 |
4299 | // -- Multibyte Compatibility functions ---------------------------------------
4300 | // http://svn.iphonewebdev.com/lace/lib/mb_compat.php
4301 |
4302 | /**
4303 | * mb_internal_encoding()
4304 | *
4305 | * Included for mbstring pseudo-compatability.
4306 | */
4307 | if (!function_exists('mb_internal_encoding'))
4308 | {
4309 | function mb_internal_encoding($enc) {return true; }
4310 | }
4311 |
4312 | /**
4313 | * mb_regex_encoding()
4314 | *
4315 | * Included for mbstring pseudo-compatability.
4316 | */
4317 | if (!function_exists('mb_regex_encoding'))
4318 | {
4319 | function mb_regex_encoding($enc) {return true; }
4320 | }
4321 |
4322 | /**
4323 | * mb_strlen()
4324 | *
4325 | * Included for mbstring pseudo-compatability.
4326 | */
4327 | if (!function_exists('mb_strlen'))
4328 | {
4329 | function mb_strlen($str)
4330 | {
4331 | return strlen($str);
4332 | }
4333 | }
4334 |
4335 | /**
4336 | * mb_strpos()
4337 | *
4338 | * Included for mbstring pseudo-compatability.
4339 | */
4340 | if (!function_exists('mb_strpos'))
4341 | {
4342 | function mb_strpos($haystack, $needle, $offset=0)
4343 | {
4344 | return strpos($haystack, $needle, $offset);
4345 | }
4346 | }
4347 | /**
4348 | * mb_stripos()
4349 | *
4350 | * Included for mbstring pseudo-compatability.
4351 | */
4352 | if (!function_exists('mb_stripos'))
4353 | {
4354 | function mb_stripos($haystack, $needle, $offset=0)
4355 | {
4356 | return stripos($haystack, $needle, $offset);
4357 | }
4358 | }
4359 |
4360 | /**
4361 | * mb_substr()
4362 | *
4363 | * Included for mbstring pseudo-compatability.
4364 | */
4365 | if (!function_exists('mb_substr'))
4366 | {
4367 | function mb_substr($str, $start, $length=0)
4368 | {
4369 | return substr($str, $start, $length);
4370 | }
4371 | }
4372 |
4373 | /**
4374 | * mb_substr_count()
4375 | *
4376 | * Included for mbstring pseudo-compatability.
4377 | */
4378 | if (!function_exists('mb_substr_count'))
4379 | {
4380 | function mb_substr_count($haystack, $needle)
4381 | {
4382 | return substr_count($haystack, $needle);
4383 | }
4384 | }
4385 |
4386 |
4387 | /**
4388 | * Static namespace for phpQuery functions.
4389 | *
4390 | * @author Tobiasz Cudnik
4391 | * @package phpQuery
4392 | */
4393 | abstract class phpQuery {
4394 | /**
4395 | * XXX: Workaround for mbstring problems
4396 | *
4397 | * @var bool
4398 | */
4399 | public static $mbstringSupport = true;
4400 | public static $debug = false;
4401 | public static $documents = array();
4402 | public static $defaultDocumentID = null;
4403 | // public static $defaultDoctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"';
4404 | /**
4405 | * Applies only to HTML.
4406 | *
4407 | * @var unknown_type
4408 | */
4409 | public static $defaultDoctype = '';
4411 | public static $defaultCharset = 'UTF-8';
4412 | /**
4413 | * Static namespace for plugins.
4414 | *
4415 | * @var object
4416 | */
4417 | public static $plugins = array();
4418 | /**
4419 | * List of loaded plugins.
4420 | *
4421 | * @var unknown_type
4422 | */
4423 | public static $pluginsLoaded = array();
4424 | public static $pluginsMethods = array();
4425 | public static $pluginsStaticMethods = array();
4426 | public static $extendMethods = array();
4427 | /**
4428 | * @TODO implement
4429 | */
4430 | public static $extendStaticMethods = array();
4431 | /**
4432 | * Hosts allowed for AJAX connections.
4433 | * Dot '.' means $_SERVER['HTTP_HOST'] (if any).
4434 | *
4435 | * @var array
4436 | */
4437 | public static $ajaxAllowedHosts = array(
4438 | '.'
4439 | );
4440 | /**
4441 | * AJAX settings.
4442 | *
4443 | * @var array
4444 | * XXX should it be static or not ?
4445 | */
4446 | public static $ajaxSettings = array(
4447 | 'url' => '',//TODO
4448 | 'global' => true,
4449 | 'type' => "GET",
4450 | 'timeout' => null,
4451 | 'contentType' => "application/x-www-form-urlencoded",
4452 | 'processData' => true,
4453 | // 'async' => true,
4454 | 'data' => null,
4455 | 'username' => null,
4456 | 'password' => null,
4457 | 'accepts' => array(
4458 | 'xml' => "application/xml, text/xml",
4459 | 'html' => "text/html",
4460 | 'script' => "text/javascript, application/javascript",
4461 | 'json' => "application/json, text/javascript",
4462 | 'text' => "text/plain",
4463 | '_default' => "*/*"
4464 | )
4465 | );
4466 | public static $lastModified = null;
4467 | public static $active = 0;
4468 | public static $dumpCount = 0;
4469 | /**
4470 | * Multi-purpose function.
4471 | * Use pq() as shortcut.
4472 | *
4473 | * In below examples, $pq is any result of pq(); function.
4474 | *
4475 | * 1. Import markup into existing document (without any attaching):
4476 | * - Import into selected document:
4477 | * pq('') // DOESNT accept text nodes at beginning of input string !
4478 | * - Import into document with ID from $pq->getDocumentID():
4479 | * pq('', $pq->getDocumentID())
4480 | * - Import into same document as DOMNode belongs to:
4481 | * pq('', DOMNode)
4482 | * - Import into document from phpQuery object:
4483 | * pq('', $pq)
4484 | *
4485 | * 2. Run query:
4486 | * - Run query on last selected document:
4487 | * pq('div.myClass')
4488 | * - Run query on document with ID from $pq->getDocumentID():
4489 | * pq('div.myClass', $pq->getDocumentID())
4490 | * - Run query on same document as DOMNode belongs to and use node(s)as root for query:
4491 | * pq('div.myClass', DOMNode)
4492 | * - Run query on document from phpQuery object
4493 | * and use object's stack as root node(s) for query:
4494 | * pq('div.myClass', $pq)
4495 | *
4496 | * @param string|DOMNode|DOMNodeList|array $arg1 HTML markup, CSS Selector, DOMNode or array of DOMNodes
4497 | * @param string|phpQueryObject|DOMNode $context DOM ID from $pq->getDocumentID(), phpQuery object (determines also query root) or DOMNode (determines also query root)
4498 | *
4499 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery|QueryTemplatesPhpQuery|false
4500 | * phpQuery object or false in case of error.
4501 | */
4502 | public static function pq($arg1, $context = null) {
4503 | if ($arg1 instanceof DOMNODE && ! isset($context)) {
4504 | foreach(phpQuery::$documents as $documentWrapper) {
4505 | $compare = $arg1 instanceof DOMDocument
4506 | ? $arg1 : $arg1->ownerDocument;
4507 | if ($documentWrapper->document->isSameNode($compare))
4508 | $context = $documentWrapper->id;
4509 | }
4510 | }
4511 | if (! $context) {
4512 | $domId = self::$defaultDocumentID;
4513 | if (! $domId)
4514 | throw new Exception("Can't use last created DOM, because there isn't any. Use phpQuery::newDocument() first.");
4515 | // } else if (is_object($context) && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
4516 | } else if (is_object($context) && $context instanceof phpQueryObject)
4517 | $domId = $context->getDocumentID();
4518 | else if ($context instanceof DOMDOCUMENT) {
4519 | $domId = self::getDocumentID($context);
4520 | if (! $domId) {
4521 | //throw new Exception('Orphaned DOMDocument');
4522 | $domId = self::newDocument($context)->getDocumentID();
4523 | }
4524 | } else if ($context instanceof DOMNODE) {
4525 | $domId = self::getDocumentID($context);
4526 | if (! $domId) {
4527 | throw new Exception('Orphaned DOMNode');
4528 | // $domId = self::newDocument($context->ownerDocument);
4529 | }
4530 | } else
4531 | $domId = $context;
4532 | if ($arg1 instanceof phpQueryObject) {
4533 | // if (is_object($arg1) && (get_class($arg1) == 'phpQueryObject' || $arg1 instanceof PHPQUERY || is_subclass_of($arg1, 'phpQueryObject'))) {
4534 | /**
4535 | * Return $arg1 or import $arg1 stack if document differs:
4536 | * pq(pq(''))
4537 | */
4538 | if ($arg1->getDocumentID() == $domId)
4539 | return $arg1;
4540 | $class = get_class($arg1);
4541 | // support inheritance by passing old object to overloaded constructor
4542 | $phpQuery = $class != 'phpQuery'
4543 | ? new $class($arg1, $domId)
4544 | : new phpQueryObject($domId);
4545 | $phpQuery->elements = array();
4546 | foreach($arg1->elements as $node)
4547 | $phpQuery->elements[] = $phpQuery->document->importNode($node, true);
4548 | return $phpQuery;
4549 | } else if ($arg1 instanceof DOMNODE || (is_array($arg1) && isset($arg1[0]) && $arg1[0] instanceof DOMNODE)) {
4550 | /*
4551 | * Wrap DOM nodes with phpQuery object, import into document when needed:
4552 | * pq(array($domNode1, $domNode2))
4553 | */
4554 | $phpQuery = new phpQueryObject($domId);
4555 | if (!($arg1 instanceof DOMNODELIST) && ! is_array($arg1))
4556 | $arg1 = array($arg1);
4557 | $phpQuery->elements = array();
4558 | foreach($arg1 as $node) {
4559 | $sameDocument = $node->ownerDocument instanceof DOMDOCUMENT
4560 | && ! $node->ownerDocument->isSameNode($phpQuery->document);
4561 | $phpQuery->elements[] = $sameDocument
4562 | ? $phpQuery->document->importNode($node, true)
4563 | : $node;
4564 | }
4565 | return $phpQuery;
4566 | } else if (self::isMarkup($arg1)) {
4567 | /**
4568 | * Import HTML:
4569 | * pq('')
4570 | */
4571 | $phpQuery = new phpQueryObject($domId);
4572 | return $phpQuery->newInstance(
4573 | $phpQuery->documentWrapper->import($arg1)
4574 | );
4575 | } else {
4576 | /**
4577 | * Run CSS query:
4578 | * pq('div.myClass')
4579 | */
4580 | $phpQuery = new phpQueryObject($domId);
4581 | // if ($context && ($context instanceof PHPQUERY || is_subclass_of($context, 'phpQueryObject')))
4582 | if ($context && $context instanceof phpQueryObject)
4583 | $phpQuery->elements = $context->elements;
4584 | else if ($context && $context instanceof DOMNODELIST) {
4585 | $phpQuery->elements = array();
4586 | foreach($context as $node)
4587 | $phpQuery->elements[] = $node;
4588 | } else if ($context && $context instanceof DOMNODE)
4589 | $phpQuery->elements = array($context);
4590 | return $phpQuery->find($arg1);
4591 | }
4592 | }
4593 | /**
4594 | * Sets default document to $id. Document has to be loaded prior
4595 | * to using this method.
4596 | * $id can be retrived via getDocumentID() or getDocumentIDRef().
4597 | *
4598 | * @param unknown_type $id
4599 | */
4600 | public static function selectDocument($id) {
4601 | $id = self::getDocumentID($id);
4602 | self::debug("Selecting document '$id' as default one");
4603 | self::$defaultDocumentID = self::getDocumentID($id);
4604 | }
4605 | /**
4606 | * Returns document with id $id or last used as phpQueryObject.
4607 | * $id can be retrived via getDocumentID() or getDocumentIDRef().
4608 | * Chainable.
4609 | *
4610 | * @see phpQuery::selectDocument()
4611 | * @param unknown_type $id
4612 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4613 | */
4614 | public static function getDocument($id = null) {
4615 | if ($id)
4616 | phpQuery::selectDocument($id);
4617 | else
4618 | $id = phpQuery::$defaultDocumentID;
4619 | return new phpQueryObject($id);
4620 | }
4621 | /**
4622 | * Creates new document from markup.
4623 | * Chainable.
4624 | *
4625 | * @param unknown_type $markup
4626 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4627 | */
4628 | public static function newDocument($markup = null, $contentType = null) {
4629 | if (! $markup)
4630 | $markup = '';
4631 | $documentID = phpQuery::createDocumentWrapper($markup, $contentType);
4632 | return new phpQueryObject($documentID);
4633 | }
4634 | /**
4635 | * Creates new document from markup.
4636 | * Chainable.
4637 | *
4638 | * @param unknown_type $markup
4639 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4640 | */
4641 | public static function newDocumentHTML($markup = null, $charset = null) {
4642 | $contentType = $charset
4643 | ? ";charset=$charset"
4644 | : '';
4645 | return self::newDocument($markup, "text/html{$contentType}");
4646 | }
4647 | /**
4648 | * Creates new document from markup.
4649 | * Chainable.
4650 | *
4651 | * @param unknown_type $markup
4652 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4653 | */
4654 | public static function newDocumentXML($markup = null, $charset = null) {
4655 | $contentType = $charset
4656 | ? ";charset=$charset"
4657 | : '';
4658 | return self::newDocument($markup, "text/xml{$contentType}");
4659 | }
4660 | /**
4661 | * Creates new document from markup.
4662 | * Chainable.
4663 | *
4664 | * @param unknown_type $markup
4665 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4666 | */
4667 | public static function newDocumentXHTML($markup = null, $charset = null) {
4668 | $contentType = $charset
4669 | ? ";charset=$charset"
4670 | : '';
4671 | return self::newDocument($markup, "application/xhtml+xml{$contentType}");
4672 | }
4673 | /**
4674 | * Creates new document from markup.
4675 | * Chainable.
4676 | *
4677 | * @param unknown_type $markup
4678 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4679 | */
4680 | public static function newDocumentPHP($markup = null, $contentType = "text/html") {
4681 | // TODO pass charset to phpToMarkup if possible (use DOMDocumentWrapper function)
4682 | $markup = phpQuery::phpToMarkup($markup, self::$defaultCharset);
4683 | return self::newDocument($markup, $contentType);
4684 | }
4685 | public static function phpToMarkup($php, $charset = 'utf-8') {
4686 | $regexes = array(
4687 | '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)<'.'?php?(.*?)(?:\\?>)([^\']*)\'@s',
4688 | '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)<'.'?php?(.*?)(?:\\?>)([^"]*)"@s',
4689 | );
4690 | foreach($regexes as $regex)
4691 | while (preg_match($regex, $php, $matches)) {
4692 | $php = preg_replace_callback(
4693 | $regex,
4694 | // create_function('$m, $charset = "'.$charset.'"',
4695 | // 'return $m[1].$m[2]
4696 | // .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
4697 | // .$m[5].$m[2];'
4698 | // ),
4699 | array('phpQuery', '_phpToMarkupCallback'),
4700 | $php
4701 | );
4702 | }
4703 | $regex = '@(^|>[^<]*)+?(<\?php(.*?)(\?>))@s';
4704 | //preg_match_all($regex, $php, $matches);
4705 | //var_dump($matches);
4706 | $php = preg_replace($regex, '\\1', $php);
4707 | return $php;
4708 | }
4709 | public static function _phpToMarkupCallback($php, $charset = 'utf-8') {
4710 | return $m[1].$m[2]
4711 | .htmlspecialchars("<"."?php".$m[4]."?".">", ENT_QUOTES|ENT_NOQUOTES, $charset)
4712 | .$m[5].$m[2];
4713 | }
4714 | public static function _markupToPHPCallback($m) {
4715 | return "<"."?php ".htmlspecialchars_decode($m[1])." ?".">";
4716 | }
4717 | /**
4718 | * Converts document markup containing PHP code generated by phpQuery::php()
4719 | * into valid (executable) PHP code syntax.
4720 | *
4721 | * @param string|phpQueryObject $content
4722 | * @return string PHP code.
4723 | */
4724 | public static function markupToPHP($content) {
4725 | if ($content instanceof phpQueryObject)
4726 | $content = $content->markupOuter();
4727 | /* ... to */
4728 | $content = preg_replace_callback(
4729 | '@\s*\s*@s',
4730 | // create_function('$m',
4731 | // 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
4732 | // ),
4733 | array('phpQuery', '_markupToPHPCallback'),
4734 | $content
4735 | );
4736 | /* extra space added to save highlighters */
4737 | $regexes = array(
4738 | '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(\')([^\']*)(?:<|%3C)\\?(?:php)?(.*?)(?:\\?(?:>|%3E))([^\']*)\'@s',
4739 | '@(<(?!\\?)(?:[^>]|\\?>)+\\w+\\s*=\\s*)(")([^"]*)(?:<|%3C)\\?(?:php)?(.*?)(?:\\?(?:>|%3E))([^"]*)"@s',
4740 | );
4741 | foreach($regexes as $regex)
4742 | while (preg_match($regex, $content))
4743 | $content = preg_replace_callback(
4744 | $regex,
4745 | create_function('$m',
4746 | 'return $m[1].$m[2].$m[3]."", " ", "\n", " ", "{", "$", "}", \'"\', "[", "]"),
4750 | htmlspecialchars_decode($m[4])
4751 | )
4752 | ." ?>".$m[5].$m[2];'
4753 | ),
4754 | $content
4755 | );
4756 | return $content;
4757 | }
4758 | /**
4759 | * Creates new document from file $file.
4760 | * Chainable.
4761 | *
4762 | * @param string $file URLs allowed. See File wrapper page at php.net for more supported sources.
4763 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4764 | */
4765 | public static function newDocumentFile($file, $contentType = null) {
4766 | $documentID = self::createDocumentWrapper(
4767 | file_get_contents($file), $contentType
4768 | );
4769 | return new phpQueryObject($documentID);
4770 | }
4771 | /**
4772 | * Creates new document from markup.
4773 | * Chainable.
4774 | *
4775 | * @param unknown_type $markup
4776 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4777 | */
4778 | public static function newDocumentFileHTML($file, $charset = null) {
4779 | $contentType = $charset
4780 | ? ";charset=$charset"
4781 | : '';
4782 | return self::newDocumentFile($file, "text/html{$contentType}");
4783 | }
4784 | /**
4785 | * Creates new document from markup.
4786 | * Chainable.
4787 | *
4788 | * @param unknown_type $markup
4789 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4790 | */
4791 | public static function newDocumentFileXML($file, $charset = null) {
4792 | $contentType = $charset
4793 | ? ";charset=$charset"
4794 | : '';
4795 | return self::newDocumentFile($file, "text/xml{$contentType}");
4796 | }
4797 | /**
4798 | * Creates new document from markup.
4799 | * Chainable.
4800 | *
4801 | * @param unknown_type $markup
4802 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4803 | */
4804 | public static function newDocumentFileXHTML($file, $charset = null) {
4805 | $contentType = $charset
4806 | ? ";charset=$charset"
4807 | : '';
4808 | return self::newDocumentFile($file, "application/xhtml+xml{$contentType}");
4809 | }
4810 | /**
4811 | * Creates new document from markup.
4812 | * Chainable.
4813 | *
4814 | * @param unknown_type $markup
4815 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4816 | */
4817 | public static function newDocumentFilePHP($file, $contentType = null) {
4818 | return self::newDocumentPHP(file_get_contents($file), $contentType);
4819 | }
4820 | /**
4821 | * Reuses existing DOMDocument object.
4822 | * Chainable.
4823 | *
4824 | * @param $document DOMDocument
4825 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
4826 | * @TODO support DOMDocument
4827 | */
4828 | public static function loadDocument($document) {
4829 | // TODO
4830 | die('TODO loadDocument');
4831 | }
4832 | /**
4833 | * Enter description here...
4834 | *
4835 | * @param unknown_type $html
4836 | * @param unknown_type $domId
4837 | * @return unknown New DOM ID
4838 | * @todo support PHP tags in input
4839 | * @todo support passing DOMDocument object from self::loadDocument
4840 | */
4841 | protected static function createDocumentWrapper($html, $contentType = null, $documentID = null) {
4842 | if (function_exists('domxml_open_mem'))
4843 | throw new Exception("Old PHP4 DOM XML extension detected. phpQuery won't work until this extension is enabled.");
4844 | // $id = $documentID
4845 | // ? $documentID
4846 | // : md5(microtime());
4847 | $document = null;
4848 | if ($html instanceof DOMDOCUMENT) {
4849 | if (self::getDocumentID($html)) {
4850 | // document already exists in phpQuery::$documents, make a copy
4851 | $document = clone $html;
4852 | } else {
4853 | // new document, add it to phpQuery::$documents
4854 | $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
4855 | }
4856 | } else {
4857 | $wrapper = new DOMDocumentWrapper($html, $contentType, $documentID);
4858 | }
4859 | // $wrapper->id = $id;
4860 | // bind document
4861 | phpQuery::$documents[$wrapper->id] = $wrapper;
4862 | // remember last loaded document
4863 | phpQuery::selectDocument($wrapper->id);
4864 | return $wrapper->id;
4865 | }
4866 | /**
4867 | * Extend class namespace.
4868 | *
4869 | * @param string|array $target
4870 | * @param array $source
4871 | * @TODO support string $source
4872 | * @return unknown_type
4873 | */
4874 | public static function extend($target, $source) {
4875 | switch($target) {
4876 | case 'phpQueryObject':
4877 | $targetRef = &self::$extendMethods;
4878 | $targetRef2 = &self::$pluginsMethods;
4879 | break;
4880 | case 'phpQuery':
4881 | $targetRef = &self::$extendStaticMethods;
4882 | $targetRef2 = &self::$pluginsStaticMethods;
4883 | break;
4884 | default:
4885 | throw new Exception("Unsupported \$target type");
4886 | }
4887 | if (is_string($source))
4888 | $source = array($source => $source);
4889 | foreach($source as $method => $callback) {
4890 | if (isset($targetRef[$method])) {
4891 | // throw new Exception
4892 | self::debug("Duplicate method '{$method}', can\'t extend '{$target}'");
4893 | continue;
4894 | }
4895 | if (isset($targetRef2[$method])) {
4896 | // throw new Exception
4897 | self::debug("Duplicate method '{$method}' from plugin '{$targetRef2[$method]}',"
4898 | ." can\'t extend '{$target}'");
4899 | continue;
4900 | }
4901 | $targetRef[$method] = $callback;
4902 | }
4903 | return true;
4904 | }
4905 | /**
4906 | * Extend phpQuery with $class from $file.
4907 | *
4908 | * @param string $class Extending class name. Real class name can be prepended phpQuery_.
4909 | * @param string $file Filename to include. Defaults to "{$class}.php".
4910 | */
4911 | public static function plugin($class, $file = null) {
4912 | // TODO $class checked agains phpQuery_$class
4913 | // if (strpos($class, 'phpQuery') === 0)
4914 | // $class = substr($class, 8);
4915 | if (in_array($class, self::$pluginsLoaded))
4916 | return true;
4917 | if (! $file)
4918 | $file = $class.'.php';
4919 | $objectClassExists = class_exists('phpQueryObjectPlugin_'.$class);
4920 | $staticClassExists = class_exists('phpQueryPlugin_'.$class);
4921 | if (! $objectClassExists && ! $staticClassExists)
4922 | require_once($file);
4923 | self::$pluginsLoaded[] = $class;
4924 | // static methods
4925 | if (class_exists('phpQueryPlugin_'.$class)) {
4926 | $realClass = 'phpQueryPlugin_'.$class;
4927 | $vars = get_class_vars($realClass);
4928 | $loop = isset($vars['phpQueryMethods'])
4929 | && ! is_null($vars['phpQueryMethods'])
4930 | ? $vars['phpQueryMethods']
4931 | : get_class_methods($realClass);
4932 | foreach($loop as $method) {
4933 | if ($method == '__initialize')
4934 | continue;
4935 | if (! is_callable(array($realClass, $method)))
4936 | continue;
4937 | if (isset(self::$pluginsStaticMethods[$method])) {
4938 | throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsStaticMethods[$method]."'");
4939 | return;
4940 | }
4941 | self::$pluginsStaticMethods[$method] = $class;
4942 | }
4943 | if (method_exists($realClass, '__initialize'))
4944 | call_user_func_array(array($realClass, '__initialize'), array());
4945 | }
4946 | // object methods
4947 | if (class_exists('phpQueryObjectPlugin_'.$class)) {
4948 | $realClass = 'phpQueryObjectPlugin_'.$class;
4949 | $vars = get_class_vars($realClass);
4950 | $loop = isset($vars['phpQueryMethods'])
4951 | && ! is_null($vars['phpQueryMethods'])
4952 | ? $vars['phpQueryMethods']
4953 | : get_class_methods($realClass);
4954 | foreach($loop as $method) {
4955 | if (! is_callable(array($realClass, $method)))
4956 | continue;
4957 | if (isset(self::$pluginsMethods[$method])) {
4958 | throw new Exception("Duplicate method '{$method}' from plugin '{$c}' conflicts with same method from plugin '".self::$pluginsMethods[$method]."'");
4959 | continue;
4960 | }
4961 | self::$pluginsMethods[$method] = $class;
4962 | }
4963 | }
4964 | return true;
4965 | }
4966 | /**
4967 | * Unloades all or specified document from memory.
4968 | *
4969 | * @param mixed $documentID @see phpQuery::getDocumentID() for supported types.
4970 | */
4971 | public static function unloadDocuments($id = null) {
4972 | if (isset($id)) {
4973 | if ($id = self::getDocumentID($id))
4974 | unset(phpQuery::$documents[$id]);
4975 | } else {
4976 | foreach(phpQuery::$documents as $k => $v) {
4977 | unset(phpQuery::$documents[$k]);
4978 | }
4979 | }
4980 | }
4981 | /**
4982 | * Parses phpQuery object or HTML result against PHP tags and makes them active.
4983 | *
4984 | * @param phpQuery|string $content
4985 | * @deprecated
4986 | * @return string
4987 | */
4988 | public static function unsafePHPTags($content) {
4989 | return self::markupToPHP($content);
4990 | }
4991 | public static function DOMNodeListToArray($DOMNodeList) {
4992 | $array = array();
4993 | if (! $DOMNodeList)
4994 | return $array;
4995 | foreach($DOMNodeList as $node)
4996 | $array[] = $node;
4997 | return $array;
4998 | }
4999 | /**
5000 | * Checks if $input is HTML string, which has to start with '<'.
5001 | *
5002 | * @deprecated
5003 | * @param String $input
5004 | * @return Bool
5005 | * @todo still used ?
5006 | */
5007 | public static function isMarkup($input) {
5008 | return ! is_array($input) && substr(trim($input), 0, 1) == '<';
5009 | }
5010 | public static function debug($text) {
5011 | if (self::$debug)
5012 | print var_dump($text);
5013 | }
5014 | /**
5015 | * Make an AJAX request.
5016 | *
5017 | * @param array See $options http://docs.jquery.com/Ajax/jQuery.ajax#toptions
5018 | * Additional options are:
5019 | * 'document' - document for global events, @see phpQuery::getDocumentID()
5020 | * 'referer' - implemented
5021 | * 'requested_with' - TODO; not implemented (X-Requested-With)
5022 | * @return Zend_Http_Client
5023 | * @link http://docs.jquery.com/Ajax/jQuery.ajax
5024 | *
5025 | * @TODO $options['cache']
5026 | * @TODO $options['processData']
5027 | * @TODO $options['xhr']
5028 | * @TODO $options['data'] as string
5029 | * @TODO XHR interface
5030 | */
5031 | public static function ajax($options = array(), $xhr = null) {
5032 | $options = array_merge(
5033 | self::$ajaxSettings, $options
5034 | );
5035 | $documentID = isset($options['document'])
5036 | ? self::getDocumentID($options['document'])
5037 | : null;
5038 | if ($xhr) {
5039 | // reuse existing XHR object, but clean it up
5040 | $client = $xhr;
5041 | // $client->setParameterPost(null);
5042 | // $client->setParameterGet(null);
5043 | $client->setAuth(false);
5044 | $client->setHeaders("If-Modified-Since", null);
5045 | $client->setHeaders("Referer", null);
5046 | $client->resetParameters();
5047 | } else {
5048 | // create new XHR object
5049 | require_once('Zend/Http/Client.php');
5050 | $client = new Zend_Http_Client();
5051 | $client->setCookieJar();
5052 | }
5053 | if (isset($options['timeout']))
5054 | $client->setConfig(array(
5055 | 'timeout' => $options['timeout'],
5056 | ));
5057 | // 'maxredirects' => 0,
5058 | foreach(self::$ajaxAllowedHosts as $k => $host)
5059 | if ($host == '.' && isset($_SERVER['HTTP_HOST']))
5060 | self::$ajaxAllowedHosts[$k] = $_SERVER['HTTP_HOST'];
5061 | $host = parse_url($options['url'], PHP_URL_HOST);
5062 | if (! in_array($host, self::$ajaxAllowedHosts)) {
5063 | throw new Exception("Request not permitted, host '$host' not present in "
5064 | ."phpQuery::\$ajaxAllowedHosts");
5065 | }
5066 | // JSONP
5067 | $jsre = "/=\\?(&|$)/";
5068 | if (isset($options['dataType']) && $options['dataType'] == 'jsonp') {
5069 | $jsonpCallbackParam = $options['jsonp']
5070 | ? $options['jsonp'] : 'callback';
5071 | if (strtolower($options['type']) == 'get') {
5072 | if (! preg_match($jsre, $options['url'])) {
5073 | $sep = strpos($options['url'], '?')
5074 | ? '&' : '?';
5075 | $options['url'] .= "$sep$jsonpCallbackParam=?";
5076 | }
5077 | } else if ($options['data']) {
5078 | $jsonp = false;
5079 | foreach($options['data'] as $n => $v) {
5080 | if ($v == '?')
5081 | $jsonp = true;
5082 | }
5083 | if (! $jsonp) {
5084 | $options['data'][$jsonpCallbackParam] = '?';
5085 | }
5086 | }
5087 | $options['dataType'] = 'json';
5088 | }
5089 | if (isset($options['dataType']) && $options['dataType'] == 'json') {
5090 | $jsonpCallback = 'json_'.md5(microtime());
5091 | $jsonpData = $jsonpUrl = false;
5092 | if ($options['data']) {
5093 | foreach($options['data'] as $n => $v) {
5094 | if ($v == '?')
5095 | $jsonpData = $n;
5096 | }
5097 | }
5098 | if (preg_match($jsre, $options['url']))
5099 | $jsonpUrl = true;
5100 | if ($jsonpData !== false || $jsonpUrl) {
5101 | // remember callback name for httpData()
5102 | $options['_jsonp'] = $jsonpCallback;
5103 | if ($jsonpData !== false)
5104 | $options['data'][$jsonpData] = $jsonpCallback;
5105 | if ($jsonpUrl)
5106 | $options['url'] = preg_replace($jsre, "=$jsonpCallback\\1", $options['url']);
5107 | }
5108 | }
5109 | $client->setUri($options['url']);
5110 | $client->setMethod(strtoupper($options['type']));
5111 | if (isset($options['referer']) && $options['referer'])
5112 | $client->setHeaders('Referer', $options['referer']);
5113 | $client->setHeaders(array(
5114 | // 'content-type' => $options['contentType'],
5115 | 'User-Agent' => 'Mozilla/5.0 (X11; U; Linux x86; en-US; rv:1.9.0.5) Gecko'
5116 | .'/2008122010 Firefox/3.0.5',
5117 | // TODO custom charset
5118 | 'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
5119 | // 'Connection' => 'keep-alive',
5120 | // 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
5121 | 'Accept-Language' => 'en-us,en;q=0.5',
5122 | ));
5123 | if ($options['username'])
5124 | $client->setAuth($options['username'], $options['password']);
5125 | if (isset($options['ifModified']) && $options['ifModified'])
5126 | $client->setHeaders("If-Modified-Since",
5127 | self::$lastModified
5128 | ? self::$lastModified
5129 | : "Thu, 01 Jan 1970 00:00:00 GMT"
5130 | );
5131 | $client->setHeaders("Accept",
5132 | isset($options['dataType'])
5133 | && isset(self::$ajaxSettings['accepts'][ $options['dataType'] ])
5134 | ? self::$ajaxSettings['accepts'][ $options['dataType'] ].", */*"
5135 | : self::$ajaxSettings['accepts']['_default']
5136 | );
5137 | // TODO $options['processData']
5138 | if ($options['data'] instanceof phpQueryObject) {
5139 | $serialized = $options['data']->serializeArray($options['data']);
5140 | $options['data'] = array();
5141 | foreach($serialized as $r)
5142 | $options['data'][ $r['name'] ] = $r['value'];
5143 | }
5144 | if (strtolower($options['type']) == 'get') {
5145 | $client->setParameterGet($options['data']);
5146 | } else if (strtolower($options['type']) == 'post') {
5147 | $client->setEncType($options['contentType']);
5148 | $client->setParameterPost($options['data']);
5149 | }
5150 | if (self::$active == 0 && $options['global'])
5151 | phpQueryEvents::trigger($documentID, 'ajaxStart');
5152 | self::$active++;
5153 | // beforeSend callback
5154 | if (isset($options['beforeSend']) && $options['beforeSend'])
5155 | phpQuery::callbackRun($options['beforeSend'], array($client));
5156 | // ajaxSend event
5157 | if ($options['global'])
5158 | phpQueryEvents::trigger($documentID, 'ajaxSend', array($client, $options));
5159 | if (phpQuery::$debug) {
5160 | self::debug("{$options['type']}: {$options['url']}\n");
5161 | self::debug("Options: ".var_export($options, true)."
\n");
5162 | // if ($client->getCookieJar())
5163 | // self::debug("Cookies: ".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."
\n");
5164 | }
5165 | // request
5166 | $response = $client->request();
5167 | if (phpQuery::$debug) {
5168 | self::debug('Status: '.$response->getStatus().' / '.$response->getMessage());
5169 | self::debug($client->getLastRequest());
5170 | self::debug($response->getHeaders());
5171 | }
5172 | if ($response->isSuccessful()) {
5173 | // XXX tempolary
5174 | self::$lastModified = $response->getHeader('Last-Modified');
5175 | $data = self::httpData($response->getBody(), $options['dataType'], $options);
5176 | if (isset($options['success']) && $options['success'])
5177 | phpQuery::callbackRun($options['success'], array($data, $response->getStatus(), $options));
5178 | if ($options['global'])
5179 | phpQueryEvents::trigger($documentID, 'ajaxSuccess', array($client, $options));
5180 | } else {
5181 | if (isset($options['error']) && $options['error'])
5182 | phpQuery::callbackRun($options['error'], array($client, $response->getStatus(), $response->getMessage()));
5183 | if ($options['global'])
5184 | phpQueryEvents::trigger($documentID, 'ajaxError', array($client, /*$response->getStatus(),*/$response->getMessage(), $options));
5185 | }
5186 | if (isset($options['complete']) && $options['complete'])
5187 | phpQuery::callbackRun($options['complete'], array($client, $response->getStatus()));
5188 | if ($options['global'])
5189 | phpQueryEvents::trigger($documentID, 'ajaxComplete', array($client, $options));
5190 | if ($options['global'] && ! --self::$active)
5191 | phpQueryEvents::trigger($documentID, 'ajaxStop');
5192 | return $client;
5193 | // if (is_null($domId))
5194 | // $domId = self::$defaultDocumentID ? self::$defaultDocumentID : false;
5195 | // return new phpQueryAjaxResponse($response, $domId);
5196 | }
5197 | protected static function httpData($data, $type, $options) {
5198 | if (isset($options['dataFilter']) && $options['dataFilter'])
5199 | $data = self::callbackRun($options['dataFilter'], array($data, $type));
5200 | if (is_string($data)) {
5201 | if ($type == "json") {
5202 | if (isset($options['_jsonp']) && $options['_jsonp']) {
5203 | $data = preg_replace('/^\s*\w+\((.*)\)\s*$/s', '$1', $data);
5204 | }
5205 | $data = self::parseJSON($data);
5206 | }
5207 | }
5208 | return $data;
5209 | }
5210 | /**
5211 | * Enter description here...
5212 | *
5213 | * @param array|phpQuery $data
5214 | *
5215 | */
5216 | public static function param($data) {
5217 | return http_build_query($data, null, '&');
5218 | }
5219 | public static function get($url, $data = null, $callback = null, $type = null) {
5220 | if (!is_array($data)) {
5221 | $callback = $data;
5222 | $data = null;
5223 | }
5224 | // TODO some array_values on this shit
5225 | return phpQuery::ajax(array(
5226 | 'type' => 'GET',
5227 | 'url' => $url,
5228 | 'data' => $data,
5229 | 'success' => $callback,
5230 | 'dataType' => $type,
5231 | ));
5232 | }
5233 | public static function post($url, $data = null, $callback = null, $type = null) {
5234 | if (!is_array($data)) {
5235 | $callback = $data;
5236 | $data = null;
5237 | }
5238 | return phpQuery::ajax(array(
5239 | 'type' => 'POST',
5240 | 'url' => $url,
5241 | 'data' => $data,
5242 | 'success' => $callback,
5243 | 'dataType' => $type,
5244 | ));
5245 | }
5246 | public static function getJSON($url, $data = null, $callback = null) {
5247 | if (!is_array($data)) {
5248 | $callback = $data;
5249 | $data = null;
5250 | }
5251 | // TODO some array_values on this shit
5252 | return phpQuery::ajax(array(
5253 | 'type' => 'GET',
5254 | 'url' => $url,
5255 | 'data' => $data,
5256 | 'success' => $callback,
5257 | 'dataType' => 'json',
5258 | ));
5259 | }
5260 | public static function ajaxSetup($options) {
5261 | self::$ajaxSettings = array_merge(
5262 | self::$ajaxSettings,
5263 | $options
5264 | );
5265 | }
5266 | public static function ajaxAllowHost($host1, $host2 = null, $host3 = null) {
5267 | $loop = is_array($host1)
5268 | ? $host1
5269 | : func_get_args();
5270 | foreach($loop as $host) {
5271 | if ($host && ! in_array($host, phpQuery::$ajaxAllowedHosts)) {
5272 | phpQuery::$ajaxAllowedHosts[] = $host;
5273 | }
5274 | }
5275 | }
5276 | public static function ajaxAllowURL($url1, $url2 = null, $url3 = null) {
5277 | $loop = is_array($url1)
5278 | ? $url1
5279 | : func_get_args();
5280 | foreach($loop as $url)
5281 | phpQuery::ajaxAllowHost(parse_url($url, PHP_URL_HOST));
5282 | }
5283 | /**
5284 | * Returns JSON representation of $data.
5285 | *
5286 | * @static
5287 | * @param mixed $data
5288 | * @return string
5289 | */
5290 | public static function toJSON($data) {
5291 | if (function_exists('json_encode'))
5292 | return json_encode($data);
5293 | require_once('Zend/Json/Encoder.php');
5294 | return Zend_Json_Encoder::encode($data);
5295 | }
5296 | /**
5297 | * Parses JSON into proper PHP type.
5298 | *
5299 | * @static
5300 | * @param string $json
5301 | * @return mixed
5302 | */
5303 | public static function parseJSON($json) {
5304 | if (function_exists('json_decode')) {
5305 | $return = json_decode(trim($json), true);
5306 | // json_decode and UTF8 issues
5307 | if (isset($return))
5308 | return $return;
5309 | }
5310 | require_once('Zend/Json/Decoder.php');
5311 | return Zend_Json_Decoder::decode($json);
5312 | }
5313 | /**
5314 | * Returns source's document ID.
5315 | *
5316 | * @param $source DOMNode|phpQueryObject
5317 | * @return string
5318 | */
5319 | public static function getDocumentID($source) {
5320 | if ($source instanceof DOMDOCUMENT) {
5321 | foreach(phpQuery::$documents as $id => $document) {
5322 | if ($source->isSameNode($document->document))
5323 | return $id;
5324 | }
5325 | } else if ($source instanceof DOMNODE) {
5326 | foreach(phpQuery::$documents as $id => $document) {
5327 | if ($source->ownerDocument->isSameNode($document->document))
5328 | return $id;
5329 | }
5330 | } else if ($source instanceof phpQueryObject)
5331 | return $source->getDocumentID();
5332 | else if (is_string($source) && isset(phpQuery::$documents[$source]))
5333 | return $source;
5334 | }
5335 | /**
5336 | * Get DOMDocument object related to $source.
5337 | * Returns null if such document doesn't exist.
5338 | *
5339 | * @param $source DOMNode|phpQueryObject|string
5340 | * @return string
5341 | */
5342 | public static function getDOMDocument($source) {
5343 | if ($source instanceof DOMDOCUMENT)
5344 | return $source;
5345 | $source = self::getDocumentID($source);
5346 | return $source
5347 | ? self::$documents[$id]['document']
5348 | : null;
5349 | }
5350 |
5351 | // UTILITIES
5352 | // http://docs.jquery.com/Utilities
5353 |
5354 | /**
5355 | *
5356 | * @return unknown_type
5357 | * @link http://docs.jquery.com/Utilities/jQuery.makeArray
5358 | */
5359 | public static function makeArray($obj) {
5360 | $array = array();
5361 | if (is_object($object) && $object instanceof DOMNODELIST) {
5362 | foreach($object as $value)
5363 | $array[] = $value;
5364 | } else if (is_object($object) && ! ($object instanceof Iterator)) {
5365 | foreach(get_object_vars($object) as $name => $value)
5366 | $array[0][$name] = $value;
5367 | } else {
5368 | foreach($object as $name => $value)
5369 | $array[0][$name] = $value;
5370 | }
5371 | return $array;
5372 | }
5373 | public static function inArray($value, $array) {
5374 | return in_array($value, $array);
5375 | }
5376 | /**
5377 | *
5378 | * @param $object
5379 | * @param $callback
5380 | * @return unknown_type
5381 | * @link http://docs.jquery.com/Utilities/jQuery.each
5382 | */
5383 | public static function each($object, $callback, $param1 = null, $param2 = null, $param3 = null) {
5384 | $paramStructure = null;
5385 | if (func_num_args() > 2) {
5386 | $paramStructure = func_get_args();
5387 | $paramStructure = array_slice($paramStructure, 2);
5388 | }
5389 | if (is_object($object) && ! ($object instanceof Iterator)) {
5390 | foreach(get_object_vars($object) as $name => $value)
5391 | phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
5392 | } else {
5393 | foreach($object as $name => $value)
5394 | phpQuery::callbackRun($callback, array($name, $value), $paramStructure);
5395 | }
5396 | }
5397 | /**
5398 | *
5399 | * @link http://docs.jquery.com/Utilities/jQuery.map
5400 | */
5401 | public static function map($array, $callback, $param1 = null, $param2 = null, $param3 = null) {
5402 | $result = array();
5403 | $paramStructure = null;
5404 | if (func_num_args() > 2) {
5405 | $paramStructure = func_get_args();
5406 | $paramStructure = array_slice($paramStructure, 2);
5407 | }
5408 | foreach($array as $v) {
5409 | $vv = phpQuery::callbackRun($callback, array($v), $paramStructure);
5410 | // $callbackArgs = $args;
5411 | // foreach($args as $i => $arg) {
5412 | // $callbackArgs[$i] = $arg instanceof CallbackParam
5413 | // ? $v
5414 | // : $arg;
5415 | // }
5416 | // $vv = call_user_func_array($callback, $callbackArgs);
5417 | if (is_array($vv)) {
5418 | foreach($vv as $vvv)
5419 | $result[] = $vvv;
5420 | } else if ($vv !== null) {
5421 | $result[] = $vv;
5422 | }
5423 | }
5424 | return $result;
5425 | }
5426 | /**
5427 | *
5428 | * @param $callback Callback
5429 | * @param $params
5430 | * @param $paramStructure
5431 | * @return unknown_type
5432 | */
5433 | public static function callbackRun($callback, $params = array(), $paramStructure = null) {
5434 | if (! $callback)
5435 | return;
5436 | if ($callback instanceof CallbackParameterToReference) {
5437 | // TODO support ParamStructure to select which $param push to reference
5438 | if (isset($params[0]))
5439 | $callback->callback = $params[0];
5440 | return true;
5441 | }
5442 | if ($callback instanceof Callback) {
5443 | $paramStructure = $callback->params;
5444 | $callback = $callback->callback;
5445 | }
5446 | if (! $paramStructure)
5447 | return call_user_func_array($callback, $params);
5448 | $p = 0;
5449 | foreach($paramStructure as $i => $v) {
5450 | $paramStructure[$i] = $v instanceof CallbackParam
5451 | ? $params[$p++]
5452 | : $v;
5453 | }
5454 | return call_user_func_array($callback, $paramStructure);
5455 | }
5456 | /**
5457 | * Merge 2 phpQuery objects.
5458 | * @param array $one
5459 | * @param array $two
5460 | * @protected
5461 | * @todo node lists, phpQueryObject
5462 | */
5463 | public static function merge($one, $two) {
5464 | $elements = $one->elements;
5465 | foreach($two->elements as $node) {
5466 | $exists = false;
5467 | foreach($elements as $node2) {
5468 | if ($node2->isSameNode($node))
5469 | $exists = true;
5470 | }
5471 | if (! $exists)
5472 | $elements[] = $node;
5473 | }
5474 | return $elements;
5475 | // $one = $one->newInstance();
5476 | // $one->elements = $elements;
5477 | // return $one;
5478 | }
5479 | /**
5480 | *
5481 | * @param $array
5482 | * @param $callback
5483 | * @param $invert
5484 | * @return unknown_type
5485 | * @link http://docs.jquery.com/Utilities/jQuery.grep
5486 | */
5487 | public static function grep($array, $callback, $invert = false) {
5488 | $result = array();
5489 | foreach($array as $k => $v) {
5490 | $r = call_user_func_array($callback, array($v, $k));
5491 | if ($r === !(bool)$invert)
5492 | $result[] = $v;
5493 | }
5494 | return $result;
5495 | }
5496 | public static function unique($array) {
5497 | return array_unique($array);
5498 | }
5499 | /**
5500 | *
5501 | * @param $function
5502 | * @return unknown_type
5503 | * @TODO there are problems with non-static methods, second parameter pass it
5504 | * but doesnt verify is method is really callable
5505 | */
5506 | public static function isFunction($function) {
5507 | return is_callable($function);
5508 | }
5509 | public static function trim($str) {
5510 | return trim($str);
5511 | }
5512 | /* PLUGINS NAMESPACE */
5513 | /**
5514 | *
5515 | * @param $url
5516 | * @param $callback
5517 | * @param $param1
5518 | * @param $param2
5519 | * @param $param3
5520 | * @return phpQueryObject
5521 | */
5522 | public static function browserGet($url, $callback, $param1 = null, $param2 = null, $param3 = null) {
5523 | if (self::plugin('WebBrowser')) {
5524 | $params = func_get_args();
5525 | return self::callbackRun(array(self::$plugins, 'browserGet'), $params);
5526 | } else {
5527 | self::debug('WebBrowser plugin not available...');
5528 | }
5529 | }
5530 | /**
5531 | *
5532 | * @param $url
5533 | * @param $data
5534 | * @param $callback
5535 | * @param $param1
5536 | * @param $param2
5537 | * @param $param3
5538 | * @return phpQueryObject
5539 | */
5540 | public static function browserPost($url, $data, $callback, $param1 = null, $param2 = null, $param3 = null) {
5541 | if (self::plugin('WebBrowser')) {
5542 | $params = func_get_args();
5543 | return self::callbackRun(array(self::$plugins, 'browserPost'), $params);
5544 | } else {
5545 | self::debug('WebBrowser plugin not available...');
5546 | }
5547 | }
5548 | /**
5549 | *
5550 | * @param $ajaxSettings
5551 | * @param $callback
5552 | * @param $param1
5553 | * @param $param2
5554 | * @param $param3
5555 | * @return phpQueryObject
5556 | */
5557 | public static function browser($ajaxSettings, $callback, $param1 = null, $param2 = null, $param3 = null) {
5558 | if (self::plugin('WebBrowser')) {
5559 | $params = func_get_args();
5560 | return self::callbackRun(array(self::$plugins, 'browser'), $params);
5561 | } else {
5562 | self::debug('WebBrowser plugin not available...');
5563 | }
5564 | }
5565 | /**
5566 | *
5567 | * @param $code
5568 | * @return string
5569 | */
5570 | public static function php($code) {
5571 | return self::code('php', $code);
5572 | }
5573 | /**
5574 | *
5575 | * @param $type
5576 | * @param $code
5577 | * @return string
5578 | */
5579 | public static function code($type, $code) {
5580 | return "<$type>$type>";
5581 | }
5582 |
5583 | public static function __callStatic($method, $params) {
5584 | return call_user_func_array(
5585 | array(phpQuery::$plugins, $method),
5586 | $params
5587 | );
5588 | }
5589 | protected static function dataSetupNode($node, $documentID) {
5590 | // search are return if alredy exists
5591 | foreach(phpQuery::$documents[$documentID]->dataNodes as $dataNode) {
5592 | if ($node->isSameNode($dataNode))
5593 | return $dataNode;
5594 | }
5595 | // if doesn't, add it
5596 | phpQuery::$documents[$documentID]->dataNodes[] = $node;
5597 | return $node;
5598 | }
5599 | protected static function dataRemoveNode($node, $documentID) {
5600 | // search are return if alredy exists
5601 | foreach(phpQuery::$documents[$documentID]->dataNodes as $k => $dataNode) {
5602 | if ($node->isSameNode($dataNode)) {
5603 | unset(self::$documents[$documentID]->dataNodes[$k]);
5604 | unset(self::$documents[$documentID]->data[ $dataNode->dataID ]);
5605 | }
5606 | }
5607 | }
5608 | public static function data($node, $name, $data, $documentID = null) {
5609 | if (! $documentID)
5610 | // TODO check if this works
5611 | $documentID = self::getDocumentID($node);
5612 | $document = phpQuery::$documents[$documentID];
5613 | $node = self::dataSetupNode($node, $documentID);
5614 | if (! isset($node->dataID))
5615 | $node->dataID = ++phpQuery::$documents[$documentID]->uuid;
5616 | $id = $node->dataID;
5617 | if (! isset($document->data[$id]))
5618 | $document->data[$id] = array();
5619 | if (! is_null($data))
5620 | $document->data[$id][$name] = $data;
5621 | if ($name) {
5622 | if (isset($document->data[$id][$name]))
5623 | return $document->data[$id][$name];
5624 | } else
5625 | return $id;
5626 | }
5627 | public static function removeData($node, $name, $documentID) {
5628 | if (! $documentID)
5629 | // TODO check if this works
5630 | $documentID = self::getDocumentID($node);
5631 | $document = phpQuery::$documents[$documentID];
5632 | $node = self::dataSetupNode($node, $documentID);
5633 | $id = $node->dataID;
5634 | if ($name) {
5635 | if (isset($document->data[$id][$name]))
5636 | unset($document->data[$id][$name]);
5637 | $name = null;
5638 | foreach($document->data[$id] as $name)
5639 | break;
5640 | if (! $name)
5641 | self::removeData($node, $name, $documentID);
5642 | } else {
5643 | self::dataRemoveNode($node, $documentID);
5644 | }
5645 | }
5646 | }
5647 | /**
5648 | * Plugins static namespace class.
5649 | *
5650 | * @author Tobiasz Cudnik
5651 | * @package phpQuery
5652 | * @todo move plugin methods here (as statics)
5653 | */
5654 | class phpQueryPlugins {
5655 | public function __call($method, $args) {
5656 | if (isset(phpQuery::$extendStaticMethods[$method])) {
5657 | $return = call_user_func_array(
5658 | phpQuery::$extendStaticMethods[$method],
5659 | $args
5660 | );
5661 | } else if (isset(phpQuery::$pluginsStaticMethods[$method])) {
5662 | $class = phpQuery::$pluginsStaticMethods[$method];
5663 | $realClass = "phpQueryPlugin_$class";
5664 | $return = call_user_func_array(
5665 | array($realClass, $method),
5666 | $args
5667 | );
5668 | return isset($return)
5669 | ? $return
5670 | : $this;
5671 | } else
5672 | throw new Exception("Method '{$method}' doesnt exist");
5673 | }
5674 | }
5675 | /**
5676 | * Shortcut to phpQuery::pq($arg1, $context)
5677 | * Chainable.
5678 | *
5679 | * @see phpQuery::pq()
5680 | * @return phpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery
5681 | * @author Tobiasz Cudnik
5682 | * @package phpQuery
5683 | */
5684 | function pq($arg1, $context = null) {
5685 | $args = func_get_args();
5686 | return call_user_func_array(
5687 | array('phpQuery', 'pq'),
5688 | $args
5689 | );
5690 | }
5691 | // add plugins dir and Zend framework to include path
5692 | set_include_path(
5693 | get_include_path()
5694 | .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/'
5695 | .PATH_SEPARATOR.dirname(__FILE__).'/phpQuery/plugins/'
5696 | );
5697 | // why ? no __call nor __get for statics in php...
5698 | // XXX __callStatic will be available in PHP 5.3
5699 | phpQuery::$plugins = new phpQueryPlugins();
5700 | // include bootstrap file (personal library config)
5701 | if (file_exists(dirname(__FILE__).'/phpQuery/bootstrap.php'))
5702 | require_once dirname(__FILE__).'/phpQuery/bootstrap.php';
--------------------------------------------------------------------------------