Version Description
- Fixed bug jQuery as dependency for js scripts
- Fixed bug "no-icon class in same window" working with javascript
- Fixed bug setting defaults on installation
Download this release
Release Info
Developer | freelancephp |
Plugin | WP External Links (nofollow new tab seo) |
Version | 1.56 |
Comparing to | |
See all releases |
Code changes from version 1.55 to 1.56
- LICENSE +20 -20
- README.md +6 -6
- includes/class-admin-external-links.php +660 -659
- includes/class-wp-external-links.php +607 -607
- includes/phpQuery.php +5702 -5702
- includes/wp-plugin-dev-classes/class-wp-meta-box-page.php +664 -664
- includes/wp-plugin-dev-classes/class-wp-option-forms.php +360 -360
- js/admin-wp-external-links.js +2 -2
- js/src/.htaccess +1 -0
- js/src/admin-wp-external-links.js +173 -173
- js/src/wp-external-links.js +69 -69
- js/src/wp-option-forms.js +55 -55
- js/wp-external-links.js +2 -2
- js/wp-option-forms.js +0 -55
- readme.txt +245 -240
- wp-external-links.php +58 -53
LICENSE
CHANGED
@@ -1,20 +1,20 @@
|
|
1 |
-
The MIT License (MIT)
|
2 |
-
|
3 |
-
Copyright (c) 2014 Victor Villaverde Laan
|
4 |
-
|
5 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6 |
-
this software and associated documentation files (the "Software"), to deal in
|
7 |
-
the Software without restriction, including without limitation the rights to
|
8 |
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9 |
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
10 |
-
subject to the following conditions:
|
11 |
-
|
12 |
-
The above copyright notice and this permission notice shall be included in all
|
13 |
-
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, FITNESS
|
17 |
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18 |
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19 |
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20 |
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1 |
+
The MIT License (MIT)
|
2 |
+
|
3 |
+
Copyright (c) 2014 Victor Villaverde Laan
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6 |
+
this software and associated documentation files (the "Software"), to deal in
|
7 |
+
the Software without restriction, including without limitation the rights to
|
8 |
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9 |
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10 |
+
subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
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, FITNESS
|
17 |
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18 |
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19 |
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20 |
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
WP-External-Links
|
2 |
-
=================
|
3 |
-
|
4 |
-
Put it on Github so everybody can contribute code changes.
|
5 |
-
|
6 |
-
See http://wordpress.org/plugins/wp-external-links/
|
1 |
+
WP-External-Links
|
2 |
+
=================
|
3 |
+
|
4 |
+
Put it on Github so everybody can contribute code changes.
|
5 |
+
|
6 |
+
See http://wordpress.org/plugins/wp-external-links/
|
includes/class-admin-external-links.php
CHANGED
@@ -1,659 +1,660 @@
|
|
1 |
-
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
-
if ( ! class_exists( 'Admin_External_Links' ) ):
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Class Admin_External_Links
|
6 |
-
* @category WordPress Plugins
|
7 |
-
*/
|
8 |
-
final class Admin_External_Links {
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Options to be saved and their default values
|
12 |
-
* @var array
|
13 |
-
*/
|
14 |
-
public $save_options = array(
|
15 |
-
'meta' => array(
|
16 |
-
'version' => NULL,
|
17 |
-
),
|
18 |
-
'main' => array(
|
19 |
-
'target' => '_none',
|
20 |
-
'filter_page' => 1,
|
21 |
-
'filter_posts' => 1,
|
22 |
-
'filter_comments' => 1,
|
23 |
-
'filter_widgets' => 1,
|
24 |
-
'ignore' => '//twitter.com/share',
|
25 |
-
),
|
26 |
-
'seo' => array(
|
27 |
-
'external' => 1,
|
28 |
-
'nofollow' => 1,
|
29 |
-
'title' => '%title%',
|
30 |
-
'use_js' => 1,
|
31 |
-
'load_in_footer' => 1,
|
32 |
-
),
|
33 |
-
'style' => array(
|
34 |
-
'class_name' => 'ext-link',
|
35 |
-
'icon' => 0,
|
36 |
-
'image_no_icon' => 1,
|
37 |
-
'no_icon_class' => 'no-ext-icon',
|
38 |
-
'no_icon_same_window' => 0,
|
39 |
-
),
|
40 |
-
'extra' => array(
|
41 |
-
'fix_js' => 0,
|
42 |
-
'phpquery' => 0,
|
43 |
-
'filter_excl_sel' => '.excl-ext-link',
|
44 |
-
),
|
45 |
-
'screen' => array(
|
46 |
-
'menu_position' => NULL,
|
47 |
-
),
|
48 |
-
);
|
49 |
-
|
50 |
-
/**
|
51 |
-
* Meta box page object
|
52 |
-
* @var WP_Meta_Box_Page
|
53 |
-
*/
|
54 |
-
public $meta_box_page = NULL;
|
55 |
-
|
56 |
-
/**
|
57 |
-
* Ajax form object
|
58 |
-
* @var WP_Ajax_Option_Form
|
59 |
-
*/
|
60 |
-
public $form = NULL;
|
61 |
-
static public $staticForm = NULL;
|
62 |
-
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Constructor
|
66 |
-
*/
|
67 |
-
public function __construct() {
|
68 |
-
$this->check_version_update();
|
69 |
-
|
70 |
-
// set meta box page
|
71 |
-
$this->meta_box_page = new WP_Meta_Box_Page_01();
|
72 |
-
|
73 |
-
// set ajax forms (also used by front-end)
|
74 |
-
$this->form = new WP_Option_Forms_01( WP_EXTERNAL_LINKS_KEY, $this->save_options );
|
75 |
-
self::$staticForm = $this->form;
|
76 |
-
|
77 |
-
// init admin
|
78 |
-
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
79 |
-
|
80 |
-
if ( is_admin() ) {
|
81 |
-
// set options for add_page_method
|
82 |
-
$menu_pos = $this->form->set_current_option( 'screen' )->value( 'menu_position' );
|
83 |
-
|
84 |
-
// init meta box page
|
85 |
-
$this->meta_box_page->init(
|
86 |
-
// settings
|
87 |
-
array(
|
88 |
-
'page_title' => $this->__( 'WP External Links' ),
|
89 |
-
'menu_title' => $this->__( 'External Links' ),
|
90 |
-
'page_slug' => strtolower( WP_EXTERNAL_LINKS_KEY ),
|
91 |
-
'add_page_method' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? 'add_submenu_page' : 'add_menu_page',
|
92 |
-
'parent_slug' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? $menu_pos : NULL,
|
93 |
-
'column_widths' => array(
|
94 |
-
1 => array( 99 ),
|
95 |
-
2 => array( 69, 29 ),
|
96 |
-
),
|
97 |
-
'icon_url' => plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ),
|
98 |
-
),
|
99 |
-
// load callback
|
100 |
-
array( $this, 'call_load_meta_box' )
|
101 |
-
);
|
102 |
-
}
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Initialize Admin
|
107 |
-
*/
|
108 |
-
public function admin_init() {
|
109 |
-
// set uninstall hook
|
110 |
-
register_uninstall_hook( WP_EXTERNAL_LINKS_FILE, array( 'Admin_External_Links', 'call_uninstall' ) );
|
111 |
-
|
112 |
-
// load text domain for translations
|
113 |
-
load_plugin_textdomain( WP_EXTERNAL_LINKS_KEY, FALSE, dirname( plugin_basename( WP_EXTERNAL_LINKS_FILE ) ) . '/lang/' );
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* Add to head of Admin page
|
118 |
-
*/
|
119 |
-
public function admin_head() {
|
120 |
-
echo <<< style
|
121 |
-
<style type="text/css">
|
122 |
-
/* WP External Links */
|
123 |
-
.postbox-container { margin-left:1%; }
|
124 |
-
.tooltip-help { text-decoration: none; }
|
125 |
-
.tipsy { padding: 5px; }
|
126 |
-
.tipsy-inner { padding: 5px 8px 4px 8px; color: white; max-width: 200px; text-align: center; text-shadow: 0 -1px 0 #333;
|
127 |
-
border-top:1px solid #808080; border-botom:1px solid #6d6d6d; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px;
|
128 |
-
background-color:#777; background-image:-ms-linear-gradient(bottom,#6d6d6d,#808080); background-image:-moz-linear-gradient(bottom,#6d6d6d,#808080); background-image:-o-linear-gradient(bottom,#6d6d6d,#808080); background-image:-webkit-gradient(linear,left bottom,left top,from(#6d6d6d),to(#808080)); background-image:-webkit-linear-gradient(bottom,#6d6d6d,#808080); background-image:linear-gradient(bottom,#6d6d6d,#808080);
|
129 |
-
}
|
130 |
-
.tipsy-north { background-position: top center; }
|
131 |
-
.tipsy-south { background-position: bottom center; }
|
132 |
-
.tipsy-east { background-position: right center; }
|
133 |
-
.tipsy-west { background-position: center bottom; }
|
134 |
-
<style>
|
135 |
-
style;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Translate text in current domain
|
140 |
-
* @param string $text
|
141 |
-
* @return string
|
142 |
-
*/
|
143 |
-
public function __( $text ) {
|
144 |
-
return translate( $text, WP_EXTERNAL_LINKS_KEY );
|
145 |
-
}
|
146 |
-
|
147 |
-
/**
|
148 |
-
* Translate text in current domain
|
149 |
-
* @param string $text
|
150 |
-
* @return string
|
151 |
-
*/
|
152 |
-
public function _e( $text ) {
|
153 |
-
echo translate( $text, WP_EXTERNAL_LINKS_KEY );
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* Load meta box action
|
158 |
-
*/
|
159 |
-
public function call_load_meta_box( $meta_box ) {
|
160 |
-
add_action( 'admin_head', array($this, 'admin_head') );
|
161 |
-
|
162 |
-
// add filters
|
163 |
-
$meta_box->add_title_filter( array( $this, 'call_page_title' ) )
|
164 |
-
->add_contextual_help_filter( array( $this, 'call_contextual_help' ) );
|
165 |
-
|
166 |
-
// add meta boxes
|
167 |
-
// add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default', $callback_args = NULL )
|
168 |
-
$meta_box->add_meta_box( $this->__( 'General Settings' ), array( $this, 'call_box_general_settings' ), 1 )
|
169 |
-
->add_meta_box( $this->__( 'SEO Settings' ), array( $this, 'call_box_seo_settings' ), 1 )
|
170 |
-
->add_meta_box( $this->__( 'Style Settings' ), array( $this, 'call_box_style_settings' ), 1 )
|
171 |
-
->add_meta_box( $this->__( 'Extra Settings' ), array( $this, 'call_box_extra_settings' ), 1 )
|
172 |
-
->add_meta_box( $this->__( 'Admin Settings' ), array( $this, 'call_box_admin_settings' ), 1 )
|
173 |
-
//->add_meta_box( $this->__( 'About this Plugin' ), array( $this, 'call_box_about' ), 2 )
|
174 |
-
->add_meta_box( $this->__( 'Other Plugins' ), array( $this, 'call_box_other_plugins' ), 2 );
|
175 |
-
|
176 |
-
// scripts
|
177 |
-
wp_enqueue_script( 'admin-wp-external-links', plugins_url( '/js/admin-wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array( 'postbox' ), WP_EXTERNAL_LINKS_VERSION );
|
178 |
-
}
|
179 |
-
|
180 |
-
/**
|
181 |
-
* Contextual_help (callback)
|
182 |
-
* @param string $content
|
183 |
-
* @return string
|
184 |
-
*/
|
185 |
-
public function call_contextual_help( $content ) {
|
186 |
-
$help = '';
|
187 |
-
$help .= $this->meta_box_page->get_ob_callback( array( $this, 'call_box_about' ) );
|
188 |
-
return $help . $content;
|
189 |
-
}
|
190 |
-
|
191 |
-
/**
|
192 |
-
* Add icon to page title
|
193 |
-
* @return string
|
194 |
-
*/
|
195 |
-
public function call_page_title( $title ) {
|
196 |
-
// when updated set the update message
|
197 |
-
if ( isset($_GET[ 'settings-updated' ]) && $_GET[ 'settings-updated' ] == 'true' ) {
|
198 |
-
$title .= '<div class="updated settings-error" id="setting-error-settings_updated">'
|
199 |
-
. '<p><strong>' . __( 'Settings saved.' ) .'</strong></p>'
|
200 |
-
. '</div>';
|
201 |
-
}
|
202 |
-
|
203 |
-
$title = '<div class="icon32" id="icon-options-custom" style="background:url( '. plugins_url( 'images/icon-wp-external-links-32.png', WP_EXTERNAL_LINKS_FILE ) .' ) no-repeat 50% 50%"><br></div>'
|
204 |
-
. $title;
|
205 |
-
|
206 |
-
return $title;
|
207 |
-
}
|
208 |
-
|
209 |
-
/**
|
210 |
-
* Meta Box: General Settings
|
211 |
-
*/
|
212 |
-
public function call_box_general_settings() {
|
213 |
-
echo $this->form->set_current_option( 'main' )->open_form();
|
214 |
-
?>
|
215 |
-
<fieldset class="options">
|
216 |
-
<table class="form-table">
|
217 |
-
<tr>
|
218 |
-
<th style="width:250px;"><?php $this->_e( 'Open external links in...' ) ?>
|
219 |
-
<?php echo $this->tooltip_help( 'Specify the target (window or tab) for opening external links.' ) ?></th>
|
220 |
-
<td class="target_external_links">
|
221 |
-
<label><?php echo $this->form->radio( 'target', '_none', array( 'class' => 'field_target' ) ); ?>
|
222 |
-
<span><?php $this->_e( 'Same window or tab (<code>_none</code>)' ) ?></span></label>
|
223 |
-
<?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the same frame.' ) ?>
|
224 |
-
<br/>
|
225 |
-
<label><?php echo $this->form->radio( 'target', '_blank', array( 'class' => 'field_target' ) ); ?>
|
226 |
-
<span><?php $this->_e( 'New window or tab (<code>_blank</code>)' ) ?></span></label>
|
227 |
-
<?php echo $this->tooltip_help( 'Open every external link in a new window or tab.' ) ?>
|
228 |
-
<br style="margin-bottom:15px;" />
|
229 |
-
<label><?php echo $this->form->radio( 'target', '_top', array( 'class' => 'field_target' ) ); ?>
|
230 |
-
<span><?php $this->_e( 'Topmost frame (<code>_top</code>)' ) ?></span></label>
|
231 |
-
<?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the topmost frame.' ) ?>
|
232 |
-
<br/>
|
233 |
-
<label><?php echo $this->form->radio( 'target', '_new', array( 'class' => 'field_target' ) ); ?>
|
234 |
-
<span><?php $this->_e( 'Seperate window or tab (<code>_new</code>)' ) ?></span></label>
|
235 |
-
<?php echo $this->tooltip_help( 'Open new window the first time and use this window for each external link.' ) ?>
|
236 |
-
</td>
|
237 |
-
</tr>
|
238 |
-
<tr>
|
239 |
-
<th style="width:250px;"><?php $this->_e( 'Apply plugin settings on...' ) ?>
|
240 |
-
<?php echo $this->tooltip_help( 'Choose contents for applying settings to external links.' ) ?></th>
|
241 |
-
<td>
|
242 |
-
<label><?php echo $this->form->checkbox( 'filter_page', 1 ); ?>
|
243 |
-
<span><?php $this->_e( 'All contents' ) ?></span> <span class="description"><?php $this->_e('(the whole <code><body></code>)') ?></span></label>
|
244 |
-
<br/> <label><?php echo $this->form->checkbox( 'filter_posts', 1 ); ?>
|
245 |
-
<span><?php $this->_e( 'Post contents' ) ?></span></label>
|
246 |
-
<br/> <label><?php echo $this->form->checkbox( 'filter_comments', 1 ); ?>
|
247 |
-
<span><?php $this->_e( 'Comments' ) ?></span></label>
|
248 |
-
<br/> <label><?php echo $this->form->checkbox( 'filter_widgets', 1 ); ?>
|
249 |
-
<span><?php
|
250 |
-
if ( self::check_widget_content_filter() ):
|
251 |
-
$this->_e( 'All widgets' );
|
252 |
-
echo $this->tooltip_help( 'Applied to all widgets by using the "widget_content" filter of the Widget Logic plugin' );
|
253 |
-
else:
|
254 |
-
$this->_e( 'All text widgets' );
|
255 |
-
echo $this->tooltip_help( 'Only the text widget will be applied. To apply to all widget you should select "All contents" option.' );
|
256 |
-
endif;
|
257 |
-
?></span></label>
|
258 |
-
</td>
|
259 |
-
</tr>
|
260 |
-
<tr>
|
261 |
-
<th><?php $this->_e( 'Ignore links (URL) containing...' ) ?>
|
262 |
-
<?php echo $this->tooltip_help( 'This plugin will completely ignore links that contain one of the given texts in the URL. Use enter to seperate each text. This check is not case sensitive.' ) ?></th>
|
263 |
-
<td><label><?php echo $this->form->textarea( 'ignore' ); ?>
|
264 |
-
<span class="description"><?php _e( 'Be as specific as you want, f.e.: <code>twitter.com</code> or <code>https://twitter.com</code>. Seperate each by an enter.' ) ?></span></label>
|
265 |
-
</td>
|
266 |
-
</tr>
|
267 |
-
</table>
|
268 |
-
</fieldset>
|
269 |
-
|
270 |
-
<?php
|
271 |
-
echo $this->form->submit();
|
272 |
-
echo $this->form->close_form();
|
273 |
-
}
|
274 |
-
|
275 |
-
/**
|
276 |
-
* Meta Box: SEO Settings
|
277 |
-
*/
|
278 |
-
public function call_box_seo_settings() {
|
279 |
-
echo $this->form->set_current_option( 'seo' )->open_form();
|
280 |
-
?>
|
281 |
-
<fieldset class="options">
|
282 |
-
<table class="form-table">
|
283 |
-
<tr>
|
284 |
-
<th style="width:250px;"><?php $this->_e( 'Add to <code>rel</code>-attribute' ) ?>
|
285 |
-
<?php echo $this->tooltip_help( 'Set values for the "rel"-atribute of external links.' ) ?></th>
|
286 |
-
<td><label><?php echo $this->form->checkbox( 'nofollow', 1 ); ?>
|
287 |
-
<span><?php $this->_e( 'Add <code>"nofollow"</code>' ) ?></span></label>
|
288 |
-
<?php echo $this->tooltip_help( 'Add "nofollow" to the "rel"-attribute of external links (unless link already has "follow").' ) ?>
|
289 |
-
<br/>
|
290 |
-
<label><?php echo $this->form->checkbox( 'external', 1 ); ?>
|
291 |
-
<span><?php $this->_e( 'Add <code>"external"</code>' ) ?></span></label>
|
292 |
-
<?php echo $this->tooltip_help( 'Add "external" to the "rel"-attribute of external links.' ) ?>
|
293 |
-
</td>
|
294 |
-
</tr>
|
295 |
-
<tr>
|
296 |
-
<th><?php $this->_e( 'Set <code>title</code>-attribute' ) ?>
|
297 |
-
<?php echo $this->tooltip_help( 'Set title attribute for external links. Use %title% for the original title value.' ) ?></th>
|
298 |
-
<td><label><?php echo $this->form->text( 'title' ); ?>
|
299 |
-
<br/><span class="description"><?php _e( 'Use <code>%title%</code> for the original title value.' ) ?></span></label></td>
|
300 |
-
</tr>
|
301 |
-
<tr>
|
302 |
-
<th><?php $this->_e( 'Use JavaScript method' ) ?>
|
303 |
-
<?php echo $this->tooltip_help( 'Enable this option to use the JavaScript method for opening links, which prevents adding target attribute in the HTML code.' ) ?></label>
|
304 |
-
</th>
|
305 |
-
<td>
|
306 |
-
<label><?php echo $this->form->checkbox( 'use_js', 1, array( 'class' => 'field_use_js' ) ); ?>
|
307 |
-
<span><?php $this->_e( 'Use JavaScript for opening links' ) ?></span> <span class="description"><?php $this->_e( '(valid xhtml strict)' ) ?></span>
|
308 |
-
<br/>
|
309 |
-
<label><?php echo $this->form->checkbox( 'load_in_footer', 1, array( 'class' => 'load_in_footer' ) ); ?>
|
310 |
-
<span><?php $this->_e( 'Load JS file in footer' ) ?></span>
|
311 |
-
</td>
|
312 |
-
</tr>
|
313 |
-
</table>
|
314 |
-
</fieldset>
|
315 |
-
<?php
|
316 |
-
echo $this->form->submit();
|
317 |
-
echo $this->form->close_form();
|
318 |
-
}
|
319 |
-
|
320 |
-
/**
|
321 |
-
* Meta Box: Style Settings
|
322 |
-
*/
|
323 |
-
public function call_box_style_settings() {
|
324 |
-
echo $this->form->set_current_option( 'style' )->open_form();
|
325 |
-
?>
|
326 |
-
<fieldset class="options">
|
327 |
-
<table class="form-table">
|
328 |
-
<tr>
|
329 |
-
<th style="width:250px;"><?php $this->_e( 'Set icon for external link' ) ?>
|
330 |
-
<?php echo $this->tooltip_help( 'Set an icon that wll be shown for external links. See example on the right side.' ) ?></th>
|
331 |
-
<td>
|
332 |
-
<div>
|
333 |
-
<div style="width:15%;float:left">
|
334 |
-
<label><?php echo $this->form->radio( 'icon', 0 ); ?>
|
335 |
-
<span><?php $this->_e( 'No icon' ) ?></span></label>
|
336 |
-
<?php for ( $x = 1; $x <= 20; $x++ ): ?>
|
337 |
-
<br/>
|
338 |
-
<label title="<?php echo sprintf( $this->__( 'Icon %1$s: choose this icon to show for all external links or add the class \'ext-icon-%1$s\' to a specific link.' ), $x ) ?>">
|
339 |
-
<?php echo $this->form->radio( 'icon', $x ); ?>
|
340 |
-
<img src="<?php echo plugins_url('images/ext-icons/ext-icon-'. $x .'.png', WP_EXTERNAL_LINKS_FILE) ?>" /></label>
|
341 |
-
<?php if ( $x % 5 == 0 ): ?>
|
342 |
-
</div>
|
343 |
-
<div style="width:15%;float:left">
|
344 |
-
<?php endif; ?>
|
345 |
-
<?php endfor; ?>
|
346 |
-
</div>
|
347 |
-
<div style="width:29%;float:left;"><span class="description"><?php $this->_e( 'Example:' ) ?></span>
|
348 |
-
<br/><img src="<?php echo plugins_url( 'images/link-icon-example.png', WP_EXTERNAL_LINKS_FILE ) ?>" />
|
349 |
-
</div>
|
350 |
-
<br style="clear:both" />
|
351 |
-
</div>
|
352 |
-
</td>
|
353 |
-
</tr>
|
354 |
-
<tr>
|
355 |
-
<th><?php $this->_e( 'Skip images' ) ?>
|
356 |
-
<?php echo $this->tooltip_help( 'Don\'t show icon for external links containing images.' ) ?></th>
|
357 |
-
<td><label><?php echo $this->form->checkbox( 'image_no_icon', 1 ); ?>
|
358 |
-
<span><?php $this->_e( 'No icon for extenal links with images' ) ?></span></label>
|
359 |
-
</td>
|
360 |
-
</tr>
|
361 |
-
<tr>
|
362 |
-
<th style="width:250px;"><?php $this->_e( 'Set no-icon class' ) ?>
|
363 |
-
<?php echo $this->tooltip_help( 'Set this class for links, that should not have the external link icon.' ) ?></th>
|
364 |
-
<td><label><?php echo $this->form->text( 'no_icon_class', array( 'class' => '' ) ); ?></label>
|
365 |
-
<br/><label><?php echo $this->form->checkbox( 'no_icon_same_window', 1 ); ?>
|
366 |
-
<span><?php $this->_e( 'Always open links with no-icon class in same window or tab' ) ?></span></label>
|
367 |
-
<?php echo $this->tooltip_help( 'When enabled external links containing the no-icon class will always be opened in the current window or tab. No matter which target is set.' ) ?>
|
368 |
-
</td>
|
369 |
-
</tr>
|
370 |
-
<tr>
|
371 |
-
<th><?php $this->_e( 'Add to <code>class</code>-attribute' ) ?>
|
372 |
-
<?php echo $this->tooltip_help( 'Add one or more extra classes to the external links, seperated by a space. It is optional, else just leave field blank.' ) ?></th>
|
373 |
-
<td><label><?php echo $this->form->text( 'class_name' ); ?></label></td>
|
374 |
-
</tr>
|
375 |
-
</table>
|
376 |
-
</fieldset>
|
377 |
-
<?php
|
378 |
-
echo $this->form->submit();
|
379 |
-
echo $this->form->close_form();
|
380 |
-
}
|
381 |
-
|
382 |
-
/**
|
383 |
-
* Meta Box: Extra Settings
|
384 |
-
*/
|
385 |
-
public function call_box_extra_settings() {
|
386 |
-
echo $this->form->set_current_option( 'extra' )->open_form();
|
387 |
-
?>
|
388 |
-
<fieldset class="options">
|
389 |
-
<table class="form-table">
|
390 |
-
<tr>
|
391 |
-
<th style="width:250px;"><?php $this->_e( 'Solving problems' ) ?>
|
392 |
-
<?php echo $this->tooltip_help( 'Some options to try when a problem occurs. These options can also cause other problems, so be carefull.' ) ?></th>
|
393 |
-
<td><label><?php echo $this->form->checkbox( 'fix_js', 1 ); ?>
|
394 |
-
<span><?php $this->_e( 'Replacing <code></a></code> with <code><\/a></code> in JavaScript code.' ) ?></span></label>
|
395 |
-
<?php echo $this->tooltip_help( 'By replacing </a> with <\/a> in JavaScript code these tags will not be processed by the plugin.' ) ?>
|
396 |
-
</td>
|
397 |
-
</tr>
|
398 |
-
<tr>
|
399 |
-
<th style="width:250px;"><?php $this->_e( 'Use phpQuery library' ) ?>
|
400 |
-
<?php echo $this->tooltip_help( 'Using phpQuery library for manipulating links. This option is experimental.' ) ?></th>
|
401 |
-
<td><label><?php echo $this->form->checkbox( 'phpquery', 1 ); ?>
|
402 |
-
<span><?php $this->_e( 'Use phpQuery for parsing document.' ) ?></span>
|
403 |
-
<span class="description">(Test it first!)</span></label>
|
404 |
-
</td>
|
405 |
-
</tr>
|
406 |
-
<tr class="filter_excl_sel" <?php echo ( $this->form->value( 'phpquery' ) ) ? '' : 'style="display:none;"'; ?>>
|
407 |
-
<th><?php $this->_e( 'Do NOT apply settings on...' ) ?>
|
408 |
-
<?php echo $this->tooltip_help( 'The external links of these selection will be excluded for the settings of this plugin. Define the selection by using CSS selectors.' ) ?></th>
|
409 |
-
<td><label><?php echo $this->form->textarea( 'filter_excl_sel' ); ?>
|
410 |
-
<span class="description"><?php _e( 'Define selection by using CSS selectors, f.e.: <code>.excl-ext-link, .entry-title, #comments-title</code> (look <a href="http://code.google.com/p/phpquery/wiki/Selectors" target="_blank">here</a> for available selectors).' ) ?></span></label>
|
411 |
-
</td>
|
412 |
-
</tr>
|
413 |
-
</table>
|
414 |
-
</fieldset>
|
415 |
-
<?php
|
416 |
-
echo $this->form->submit();
|
417 |
-
echo $this->form->close_form();
|
418 |
-
}
|
419 |
-
|
420 |
-
/**
|
421 |
-
* Meta Box: Extra Settings
|
422 |
-
*/
|
423 |
-
public function call_box_admin_settings() {
|
424 |
-
echo $this->form->set_current_option( 'screen' )->open_form();
|
425 |
-
?>
|
426 |
-
<fieldset class="options">
|
427 |
-
<table class="form-table">
|
428 |
-
<tr>
|
429 |
-
<th><?php $this->_e('Admin menu position') ?>
|
430 |
-
<?php echo $this->tooltip_help( 'Change the menu position of this plugin in "Screen Options".' ) ?></th>
|
431 |
-
<td><label>
|
432 |
-
<?php
|
433 |
-
echo $this->form->select( 'menu_position', array(
|
434 |
-
'admin.php' => 'Main menu',
|
435 |
-
'index.php' => $this->__( 'Subitem of Dashboard' ),
|
436 |
-
'edit.php' => $this->__( 'Subitem of Posts' ),
|
437 |
-
'upload.php' => $this->__( 'Subitem of Media' ),
|
438 |
-
'link-manager.php' => $this->__( 'Subitem of Links' ),
|
439 |
-
'edit.php?post_type=page' => $this->__( 'Subitem of Pages' ),
|
440 |
-
'edit-comments.php' => $this->__( 'Subitem of Comments' ),
|
441 |
-
'themes.php' => $this->__( 'Subitem of Appearance' ),
|
442 |
-
'plugins.php' => $this->__( 'Subitem of Plugins' ),
|
443 |
-
'users.php' => $this->__( 'Subitem of Users' ),
|
444 |
-
'tools.php' => $this->__( 'Subitem of Tools' ),
|
445 |
-
'options-general.php' => $this->__( 'Subitem of Settings' ),
|
446 |
-
));
|
447 |
-
?>
|
448 |
-
</label></td>
|
449 |
-
</tr>
|
450 |
-
</table>
|
451 |
-
</fieldset>
|
452 |
-
<?php
|
453 |
-
echo $this->form->submit();
|
454 |
-
echo $this->form->close_form();
|
455 |
-
}
|
456 |
-
|
457 |
-
/**
|
458 |
-
* Meta Box: About...
|
459 |
-
*/
|
460 |
-
public function call_box_about() {
|
461 |
-
?>
|
462 |
-
<h4><img src="<?php echo plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ) ?>" width="16" height="16" /> <?php $this->_e( 'WP External Links' ) ?></h4>
|
463 |
-
<div>
|
464 |
-
<p><?php printf( $this->__( 'Current version: <strong>%1$s</strong>' ), WP_EXTERNAL_LINKS_VERSION ) ?></p>
|
465 |
-
<p><?php $this->_e( 'Manage external links on your site: open in new window/tab, set link icon, add "external", add "nofollow" and more.' ) ?></p>
|
466 |
-
<p><a href="http://www.freelancephp.net/contact/" target="_blank"><?php $this->_e( 'Questions or suggestions?' ) ?></a></p>
|
467 |
-
<p><?php $this->_e( 'If you like this plugin please send your rating at WordPress.org.' ) ?></p>
|
468 |
-
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-external-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-external-links-plugin/" target="_blank">FreelancePHP.net</a></p>
|
469 |
-
</div>
|
470 |
-
<?php
|
471 |
-
}
|
472 |
-
|
473 |
-
/**
|
474 |
-
* Meta Box: Other Plugins
|
475 |
-
*/
|
476 |
-
public function call_box_other_plugins() {
|
477 |
-
?>
|
478 |
-
<h4><img src="<?php echo plugins_url( 'images/icon-email-encoder-bundle-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> Email Encoder Bundle</h4>
|
479 |
-
<div>
|
480 |
-
<?php if ( is_plugin_active( 'email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
|
481 |
-
<p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=email-encoder-bundle/email-encoder-bundle.php"><?php $this->_e( 'Settings' ) ?></a></p>
|
482 |
-
<?php elseif( file_exists( WP_PLUGIN_DIR . '/email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
|
483 |
-
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
|
484 |
-
<?php else: ?>
|
485 |
-
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=Email+Encoder+Bundle+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
|
486 |
-
<?php endif; ?>
|
487 |
-
|
488 |
-
<p><?php $this->_e( 'Protect email addresses on your site from spambots and being used for spamming by using one of the encoding methods.' ) ?></p>
|
489 |
-
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/email-encoder-bundle/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/email-encoder-php-class-wp-plugin/" target="_blank">FreelancePHP.net</a></p>
|
490 |
-
</div>
|
491 |
-
|
492 |
-
<?php echo $this->hr(); ?>
|
493 |
-
|
494 |
-
<h4><img src="<?php echo plugins_url( 'images/icon-wp-mailto-links-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> WP Mailto Links</h4>
|
495 |
-
<div>
|
496 |
-
<?php if ( is_plugin_active( 'wp-mailto-links/wp-mailto-links.php' ) ): ?>
|
497 |
-
<p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=wp-mailto-links/wp-mailto-links.php"><?php $this->_e( 'Settings' ) ?></a></p>
|
498 |
-
<?php elseif( file_exists( WP_PLUGIN_DIR . '/wp-mailto-links/wp-mailto-links.php' ) ): ?>
|
499 |
-
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
|
500 |
-
<?php else: ?>
|
501 |
-
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=WP+Mailto+Links+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
|
502 |
-
<?php endif; ?>
|
503 |
-
|
504 |
-
<p><?php $this->_e( 'Manage mailto links on your site and protect emails from spambots, set mail icon and more.' ) ?></p>
|
505 |
-
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-mailto-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-mailto-links-plugin/" target="_blank">FreelancePHP.net</a></p>
|
506 |
-
</div>
|
507 |
-
<?php
|
508 |
-
}
|
509 |
-
|
510 |
-
/**
|
511 |
-
* Activation callback
|
512 |
-
*/
|
513 |
-
public function check_version_update() {
|
514 |
-
// check for version
|
515 |
-
$meta = get_option( 'wp_external_links-meta' );
|
516 |
-
if ( $meta[ 'version' ] == WP_EXTERNAL_LINKS_VERSION )
|
517 |
-
return;
|
518 |
-
|
519 |
-
// set new version
|
520 |
-
$meta[ 'version' ] = WP_EXTERNAL_LINKS_VERSION;
|
521 |
-
update_option( 'wp_external_links-meta', $meta );
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
$new_options[ 'main' ][ '
|
531 |
-
$new_options[ 'main' ][ '
|
532 |
-
$new_options[ 'main' ][ '
|
533 |
-
$new_options[ 'main' ][ '
|
534 |
-
$new_options[ '
|
535 |
-
$new_options[ 'seo' ][ '
|
536 |
-
$new_options[ 'seo' ][ '
|
537 |
-
$new_options[ '
|
538 |
-
$new_options[ 'style' ][ '
|
539 |
-
$new_options[ 'style' ][ '
|
540 |
-
$new_options[ 'style' ][ '
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
update_option( 'wp_external_links-
|
545 |
-
update_option( 'wp_external_links-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
$
|
563 |
-
$
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
$new_options[ 'main' ][ '
|
568 |
-
$new_options[ 'main' ][ '
|
569 |
-
$new_options[ 'main' ][ '
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
$new_options[ 'seo' ][ '
|
574 |
-
$new_options[ 'seo' ][ '
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
if ( isset( $style[ '
|
581 |
-
$
|
582 |
-
|
583 |
-
|
584 |
-
$new_options[ 'extra' ][ '
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
update_option( 'wp_external_links-
|
590 |
-
update_option( 'wp_external_links-
|
591 |
-
update_option( 'wp_external_links-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
*
|
621 |
-
* @
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
$text =
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
*
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
*
|
643 |
-
* @
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
|
1 |
+
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
+
if ( ! class_exists( 'Admin_External_Links' ) ):
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Admin_External_Links
|
6 |
+
* @category WordPress Plugins
|
7 |
+
*/
|
8 |
+
final class Admin_External_Links {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Options to be saved and their default values
|
12 |
+
* @var array
|
13 |
+
*/
|
14 |
+
public $save_options = array(
|
15 |
+
'meta' => array(
|
16 |
+
'version' => NULL,
|
17 |
+
),
|
18 |
+
'main' => array(
|
19 |
+
'target' => '_none',
|
20 |
+
'filter_page' => 1,
|
21 |
+
'filter_posts' => 1,
|
22 |
+
'filter_comments' => 1,
|
23 |
+
'filter_widgets' => 1,
|
24 |
+
'ignore' => '//twitter.com/share',
|
25 |
+
),
|
26 |
+
'seo' => array(
|
27 |
+
'external' => 1,
|
28 |
+
'nofollow' => 1,
|
29 |
+
'title' => '%title%',
|
30 |
+
'use_js' => 1,
|
31 |
+
'load_in_footer' => 1,
|
32 |
+
),
|
33 |
+
'style' => array(
|
34 |
+
'class_name' => 'ext-link',
|
35 |
+
'icon' => 0,
|
36 |
+
'image_no_icon' => 1,
|
37 |
+
'no_icon_class' => 'no-ext-icon',
|
38 |
+
'no_icon_same_window' => 0,
|
39 |
+
),
|
40 |
+
'extra' => array(
|
41 |
+
'fix_js' => 0,
|
42 |
+
'phpquery' => 0,
|
43 |
+
'filter_excl_sel' => '.excl-ext-link',
|
44 |
+
),
|
45 |
+
'screen' => array(
|
46 |
+
'menu_position' => NULL,
|
47 |
+
),
|
48 |
+
);
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Meta box page object
|
52 |
+
* @var WP_Meta_Box_Page
|
53 |
+
*/
|
54 |
+
public $meta_box_page = NULL;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Ajax form object
|
58 |
+
* @var WP_Ajax_Option_Form
|
59 |
+
*/
|
60 |
+
public $form = NULL;
|
61 |
+
static public $staticForm = NULL;
|
62 |
+
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Constructor
|
66 |
+
*/
|
67 |
+
public function __construct() {
|
68 |
+
$this->check_version_update();
|
69 |
+
|
70 |
+
// set meta box page
|
71 |
+
$this->meta_box_page = new WP_Meta_Box_Page_01();
|
72 |
+
|
73 |
+
// set ajax forms (also used by front-end)
|
74 |
+
$this->form = new WP_Option_Forms_01( WP_EXTERNAL_LINKS_KEY, $this->save_options );
|
75 |
+
self::$staticForm = $this->form;
|
76 |
+
|
77 |
+
// init admin
|
78 |
+
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
79 |
+
|
80 |
+
if ( is_admin() ) {
|
81 |
+
// set options for add_page_method
|
82 |
+
$menu_pos = $this->form->set_current_option( 'screen' )->value( 'menu_position' );
|
83 |
+
|
84 |
+
// init meta box page
|
85 |
+
$this->meta_box_page->init(
|
86 |
+
// settings
|
87 |
+
array(
|
88 |
+
'page_title' => $this->__( 'WP External Links' ),
|
89 |
+
'menu_title' => $this->__( 'External Links' ),
|
90 |
+
'page_slug' => strtolower( WP_EXTERNAL_LINKS_KEY ),
|
91 |
+
'add_page_method' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? 'add_submenu_page' : 'add_menu_page',
|
92 |
+
'parent_slug' => ( ! empty( $menu_pos ) AND $menu_pos != 'admin.php' ) ? $menu_pos : NULL,
|
93 |
+
'column_widths' => array(
|
94 |
+
1 => array( 99 ),
|
95 |
+
2 => array( 69, 29 ),
|
96 |
+
),
|
97 |
+
'icon_url' => plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ),
|
98 |
+
),
|
99 |
+
// load callback
|
100 |
+
array( $this, 'call_load_meta_box' )
|
101 |
+
);
|
102 |
+
}
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Initialize Admin
|
107 |
+
*/
|
108 |
+
public function admin_init() {
|
109 |
+
// set uninstall hook
|
110 |
+
register_uninstall_hook( WP_EXTERNAL_LINKS_FILE, array( 'Admin_External_Links', 'call_uninstall' ) );
|
111 |
+
|
112 |
+
// load text domain for translations
|
113 |
+
load_plugin_textdomain( WP_EXTERNAL_LINKS_KEY, FALSE, dirname( plugin_basename( WP_EXTERNAL_LINKS_FILE ) ) . '/lang/' );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Add to head of Admin page
|
118 |
+
*/
|
119 |
+
public function admin_head() {
|
120 |
+
echo <<< style
|
121 |
+
<style type="text/css">
|
122 |
+
/* WP External Links */
|
123 |
+
.postbox-container { margin-left:1%; }
|
124 |
+
.tooltip-help { text-decoration: none; }
|
125 |
+
.tipsy { padding: 5px; }
|
126 |
+
.tipsy-inner { padding: 5px 8px 4px 8px; color: white; max-width: 200px; text-align: center; text-shadow: 0 -1px 0 #333;
|
127 |
+
border-top:1px solid #808080; border-botom:1px solid #6d6d6d; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px;
|
128 |
+
background-color:#777; background-image:-ms-linear-gradient(bottom,#6d6d6d,#808080); background-image:-moz-linear-gradient(bottom,#6d6d6d,#808080); background-image:-o-linear-gradient(bottom,#6d6d6d,#808080); background-image:-webkit-gradient(linear,left bottom,left top,from(#6d6d6d),to(#808080)); background-image:-webkit-linear-gradient(bottom,#6d6d6d,#808080); background-image:linear-gradient(bottom,#6d6d6d,#808080);
|
129 |
+
}
|
130 |
+
.tipsy-north { background-position: top center; }
|
131 |
+
.tipsy-south { background-position: bottom center; }
|
132 |
+
.tipsy-east { background-position: right center; }
|
133 |
+
.tipsy-west { background-position: center bottom; }
|
134 |
+
<style>
|
135 |
+
style;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Translate text in current domain
|
140 |
+
* @param string $text
|
141 |
+
* @return string
|
142 |
+
*/
|
143 |
+
public function __( $text ) {
|
144 |
+
return translate( $text, WP_EXTERNAL_LINKS_KEY );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Translate text in current domain
|
149 |
+
* @param string $text
|
150 |
+
* @return string
|
151 |
+
*/
|
152 |
+
public function _e( $text ) {
|
153 |
+
echo translate( $text, WP_EXTERNAL_LINKS_KEY );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Load meta box action
|
158 |
+
*/
|
159 |
+
public function call_load_meta_box( $meta_box ) {
|
160 |
+
add_action( 'admin_head', array($this, 'admin_head') );
|
161 |
+
|
162 |
+
// add filters
|
163 |
+
$meta_box->add_title_filter( array( $this, 'call_page_title' ) )
|
164 |
+
->add_contextual_help_filter( array( $this, 'call_contextual_help' ) );
|
165 |
+
|
166 |
+
// add meta boxes
|
167 |
+
// add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default', $callback_args = NULL )
|
168 |
+
$meta_box->add_meta_box( $this->__( 'General Settings' ), array( $this, 'call_box_general_settings' ), 1 )
|
169 |
+
->add_meta_box( $this->__( 'SEO Settings' ), array( $this, 'call_box_seo_settings' ), 1 )
|
170 |
+
->add_meta_box( $this->__( 'Style Settings' ), array( $this, 'call_box_style_settings' ), 1 )
|
171 |
+
->add_meta_box( $this->__( 'Extra Settings' ), array( $this, 'call_box_extra_settings' ), 1 )
|
172 |
+
->add_meta_box( $this->__( 'Admin Settings' ), array( $this, 'call_box_admin_settings' ), 1 )
|
173 |
+
//->add_meta_box( $this->__( 'About this Plugin' ), array( $this, 'call_box_about' ), 2 )
|
174 |
+
->add_meta_box( $this->__( 'Other Plugins' ), array( $this, 'call_box_other_plugins' ), 2 );
|
175 |
+
|
176 |
+
// scripts
|
177 |
+
wp_enqueue_script( 'admin-wp-external-links', plugins_url( '/js/admin-wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array( 'jquery', 'postbox' ), WP_EXTERNAL_LINKS_VERSION );
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Contextual_help (callback)
|
182 |
+
* @param string $content
|
183 |
+
* @return string
|
184 |
+
*/
|
185 |
+
public function call_contextual_help( $content ) {
|
186 |
+
$help = '';
|
187 |
+
$help .= $this->meta_box_page->get_ob_callback( array( $this, 'call_box_about' ) );
|
188 |
+
return $help . $content;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Add icon to page title
|
193 |
+
* @return string
|
194 |
+
*/
|
195 |
+
public function call_page_title( $title ) {
|
196 |
+
// when updated set the update message
|
197 |
+
if ( isset($_GET[ 'settings-updated' ]) && $_GET[ 'settings-updated' ] == 'true' ) {
|
198 |
+
$title .= '<div class="updated settings-error" id="setting-error-settings_updated">'
|
199 |
+
. '<p><strong>' . __( 'Settings saved.' ) .'</strong></p>'
|
200 |
+
. '</div>';
|
201 |
+
}
|
202 |
+
|
203 |
+
$title = '<div class="icon32" id="icon-options-custom" style="background:url( '. plugins_url( 'images/icon-wp-external-links-32.png', WP_EXTERNAL_LINKS_FILE ) .' ) no-repeat 50% 50%"><br></div>'
|
204 |
+
. $title;
|
205 |
+
|
206 |
+
return $title;
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Meta Box: General Settings
|
211 |
+
*/
|
212 |
+
public function call_box_general_settings() {
|
213 |
+
echo $this->form->set_current_option( 'main' )->open_form();
|
214 |
+
?>
|
215 |
+
<fieldset class="options">
|
216 |
+
<table class="form-table">
|
217 |
+
<tr>
|
218 |
+
<th style="width:250px;"><?php $this->_e( 'Open external links in...' ) ?>
|
219 |
+
<?php echo $this->tooltip_help( 'Specify the target (window or tab) for opening external links.' ) ?></th>
|
220 |
+
<td class="target_external_links">
|
221 |
+
<label><?php echo $this->form->radio( 'target', '_none', array( 'class' => 'field_target' ) ); ?>
|
222 |
+
<span><?php $this->_e( 'Same window or tab (<code>_none</code>)' ) ?></span></label>
|
223 |
+
<?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the same frame.' ) ?>
|
224 |
+
<br/>
|
225 |
+
<label><?php echo $this->form->radio( 'target', '_blank', array( 'class' => 'field_target' ) ); ?>
|
226 |
+
<span><?php $this->_e( 'New window or tab (<code>_blank</code>)' ) ?></span></label>
|
227 |
+
<?php echo $this->tooltip_help( 'Open every external link in a new window or tab.' ) ?>
|
228 |
+
<br style="margin-bottom:15px;" />
|
229 |
+
<label><?php echo $this->form->radio( 'target', '_top', array( 'class' => 'field_target' ) ); ?>
|
230 |
+
<span><?php $this->_e( 'Topmost frame (<code>_top</code>)' ) ?></span></label>
|
231 |
+
<?php echo $this->tooltip_help( 'Open in current window or tab, when framed in the topmost frame.' ) ?>
|
232 |
+
<br/>
|
233 |
+
<label><?php echo $this->form->radio( 'target', '_new', array( 'class' => 'field_target' ) ); ?>
|
234 |
+
<span><?php $this->_e( 'Seperate window or tab (<code>_new</code>)' ) ?></span></label>
|
235 |
+
<?php echo $this->tooltip_help( 'Open new window the first time and use this window for each external link.' ) ?>
|
236 |
+
</td>
|
237 |
+
</tr>
|
238 |
+
<tr>
|
239 |
+
<th style="width:250px;"><?php $this->_e( 'Apply plugin settings on...' ) ?>
|
240 |
+
<?php echo $this->tooltip_help( 'Choose contents for applying settings to external links.' ) ?></th>
|
241 |
+
<td>
|
242 |
+
<label><?php echo $this->form->checkbox( 'filter_page', 1 ); ?>
|
243 |
+
<span><?php $this->_e( 'All contents' ) ?></span> <span class="description"><?php $this->_e('(the whole <code><body></code>)') ?></span></label>
|
244 |
+
<br/> <label><?php echo $this->form->checkbox( 'filter_posts', 1 ); ?>
|
245 |
+
<span><?php $this->_e( 'Post contents' ) ?></span></label>
|
246 |
+
<br/> <label><?php echo $this->form->checkbox( 'filter_comments', 1 ); ?>
|
247 |
+
<span><?php $this->_e( 'Comments' ) ?></span></label>
|
248 |
+
<br/> <label><?php echo $this->form->checkbox( 'filter_widgets', 1 ); ?>
|
249 |
+
<span><?php
|
250 |
+
if ( self::check_widget_content_filter() ):
|
251 |
+
$this->_e( 'All widgets' );
|
252 |
+
echo $this->tooltip_help( 'Applied to all widgets by using the "widget_content" filter of the Widget Logic plugin' );
|
253 |
+
else:
|
254 |
+
$this->_e( 'All text widgets' );
|
255 |
+
echo $this->tooltip_help( 'Only the text widget will be applied. To apply to all widget you should select "All contents" option.' );
|
256 |
+
endif;
|
257 |
+
?></span></label>
|
258 |
+
</td>
|
259 |
+
</tr>
|
260 |
+
<tr>
|
261 |
+
<th><?php $this->_e( 'Ignore links (URL) containing...' ) ?>
|
262 |
+
<?php echo $this->tooltip_help( 'This plugin will completely ignore links that contain one of the given texts in the URL. Use enter to seperate each text. This check is not case sensitive.' ) ?></th>
|
263 |
+
<td><label><?php echo $this->form->textarea( 'ignore' ); ?>
|
264 |
+
<span class="description"><?php _e( 'Be as specific as you want, f.e.: <code>twitter.com</code> or <code>https://twitter.com</code>. Seperate each by an enter.' ) ?></span></label>
|
265 |
+
</td>
|
266 |
+
</tr>
|
267 |
+
</table>
|
268 |
+
</fieldset>
|
269 |
+
|
270 |
+
<?php
|
271 |
+
echo $this->form->submit();
|
272 |
+
echo $this->form->close_form();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Meta Box: SEO Settings
|
277 |
+
*/
|
278 |
+
public function call_box_seo_settings() {
|
279 |
+
echo $this->form->set_current_option( 'seo' )->open_form();
|
280 |
+
?>
|
281 |
+
<fieldset class="options">
|
282 |
+
<table class="form-table">
|
283 |
+
<tr>
|
284 |
+
<th style="width:250px;"><?php $this->_e( 'Add to <code>rel</code>-attribute' ) ?>
|
285 |
+
<?php echo $this->tooltip_help( 'Set values for the "rel"-atribute of external links.' ) ?></th>
|
286 |
+
<td><label><?php echo $this->form->checkbox( 'nofollow', 1 ); ?>
|
287 |
+
<span><?php $this->_e( 'Add <code>"nofollow"</code>' ) ?></span></label>
|
288 |
+
<?php echo $this->tooltip_help( 'Add "nofollow" to the "rel"-attribute of external links (unless link already has "follow").' ) ?>
|
289 |
+
<br/>
|
290 |
+
<label><?php echo $this->form->checkbox( 'external', 1 ); ?>
|
291 |
+
<span><?php $this->_e( 'Add <code>"external"</code>' ) ?></span></label>
|
292 |
+
<?php echo $this->tooltip_help( 'Add "external" to the "rel"-attribute of external links.' ) ?>
|
293 |
+
</td>
|
294 |
+
</tr>
|
295 |
+
<tr>
|
296 |
+
<th><?php $this->_e( 'Set <code>title</code>-attribute' ) ?>
|
297 |
+
<?php echo $this->tooltip_help( 'Set title attribute for external links. Use %title% for the original title value.' ) ?></th>
|
298 |
+
<td><label><?php echo $this->form->text( 'title' ); ?>
|
299 |
+
<br/><span class="description"><?php _e( 'Use <code>%title%</code> for the original title value.' ) ?></span></label></td>
|
300 |
+
</tr>
|
301 |
+
<tr>
|
302 |
+
<th><?php $this->_e( 'Use JavaScript method' ) ?>
|
303 |
+
<?php echo $this->tooltip_help( 'Enable this option to use the JavaScript method for opening links, which prevents adding target attribute in the HTML code.' ) ?></label>
|
304 |
+
</th>
|
305 |
+
<td>
|
306 |
+
<label><?php echo $this->form->checkbox( 'use_js', 1, array( 'class' => 'field_use_js' ) ); ?>
|
307 |
+
<span><?php $this->_e( 'Use JavaScript for opening links' ) ?></span> <span class="description"><?php $this->_e( '(valid xhtml strict)' ) ?></span>
|
308 |
+
<br/>
|
309 |
+
<label><?php echo $this->form->checkbox( 'load_in_footer', 1, array( 'class' => 'load_in_footer' ) ); ?>
|
310 |
+
<span><?php $this->_e( 'Load JS file in footer' ) ?></span>
|
311 |
+
</td>
|
312 |
+
</tr>
|
313 |
+
</table>
|
314 |
+
</fieldset>
|
315 |
+
<?php
|
316 |
+
echo $this->form->submit();
|
317 |
+
echo $this->form->close_form();
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* Meta Box: Style Settings
|
322 |
+
*/
|
323 |
+
public function call_box_style_settings() {
|
324 |
+
echo $this->form->set_current_option( 'style' )->open_form();
|
325 |
+
?>
|
326 |
+
<fieldset class="options">
|
327 |
+
<table class="form-table">
|
328 |
+
<tr>
|
329 |
+
<th style="width:250px;"><?php $this->_e( 'Set icon for external link' ) ?>
|
330 |
+
<?php echo $this->tooltip_help( 'Set an icon that wll be shown for external links. See example on the right side.' ) ?></th>
|
331 |
+
<td>
|
332 |
+
<div>
|
333 |
+
<div style="width:15%;float:left">
|
334 |
+
<label><?php echo $this->form->radio( 'icon', 0 ); ?>
|
335 |
+
<span><?php $this->_e( 'No icon' ) ?></span></label>
|
336 |
+
<?php for ( $x = 1; $x <= 20; $x++ ): ?>
|
337 |
+
<br/>
|
338 |
+
<label title="<?php echo sprintf( $this->__( 'Icon %1$s: choose this icon to show for all external links or add the class \'ext-icon-%1$s\' to a specific link.' ), $x ) ?>">
|
339 |
+
<?php echo $this->form->radio( 'icon', $x ); ?>
|
340 |
+
<img src="<?php echo plugins_url('images/ext-icons/ext-icon-'. $x .'.png', WP_EXTERNAL_LINKS_FILE) ?>" /></label>
|
341 |
+
<?php if ( $x % 5 == 0 ): ?>
|
342 |
+
</div>
|
343 |
+
<div style="width:15%;float:left">
|
344 |
+
<?php endif; ?>
|
345 |
+
<?php endfor; ?>
|
346 |
+
</div>
|
347 |
+
<div style="width:29%;float:left;"><span class="description"><?php $this->_e( 'Example:' ) ?></span>
|
348 |
+
<br/><img src="<?php echo plugins_url( 'images/link-icon-example.png', WP_EXTERNAL_LINKS_FILE ) ?>" />
|
349 |
+
</div>
|
350 |
+
<br style="clear:both" />
|
351 |
+
</div>
|
352 |
+
</td>
|
353 |
+
</tr>
|
354 |
+
<tr>
|
355 |
+
<th><?php $this->_e( 'Skip images' ) ?>
|
356 |
+
<?php echo $this->tooltip_help( 'Don\'t show icon for external links containing images.' ) ?></th>
|
357 |
+
<td><label><?php echo $this->form->checkbox( 'image_no_icon', 1 ); ?>
|
358 |
+
<span><?php $this->_e( 'No icon for extenal links with images' ) ?></span></label>
|
359 |
+
</td>
|
360 |
+
</tr>
|
361 |
+
<tr>
|
362 |
+
<th style="width:250px;"><?php $this->_e( 'Set no-icon class' ) ?>
|
363 |
+
<?php echo $this->tooltip_help( 'Set this class for links, that should not have the external link icon.' ) ?></th>
|
364 |
+
<td><label><?php echo $this->form->text( 'no_icon_class', array( 'class' => '' ) ); ?></label>
|
365 |
+
<br/><label><?php echo $this->form->checkbox( 'no_icon_same_window', 1 ); ?>
|
366 |
+
<span><?php $this->_e( 'Always open links with no-icon class in same window or tab' ) ?></span></label>
|
367 |
+
<?php echo $this->tooltip_help( 'When enabled external links containing the no-icon class will always be opened in the current window or tab. No matter which target is set.' ) ?>
|
368 |
+
</td>
|
369 |
+
</tr>
|
370 |
+
<tr>
|
371 |
+
<th><?php $this->_e( 'Add to <code>class</code>-attribute' ) ?>
|
372 |
+
<?php echo $this->tooltip_help( 'Add one or more extra classes to the external links, seperated by a space. It is optional, else just leave field blank.' ) ?></th>
|
373 |
+
<td><label><?php echo $this->form->text( 'class_name' ); ?></label></td>
|
374 |
+
</tr>
|
375 |
+
</table>
|
376 |
+
</fieldset>
|
377 |
+
<?php
|
378 |
+
echo $this->form->submit();
|
379 |
+
echo $this->form->close_form();
|
380 |
+
}
|
381 |
+
|
382 |
+
/**
|
383 |
+
* Meta Box: Extra Settings
|
384 |
+
*/
|
385 |
+
public function call_box_extra_settings() {
|
386 |
+
echo $this->form->set_current_option( 'extra' )->open_form();
|
387 |
+
?>
|
388 |
+
<fieldset class="options">
|
389 |
+
<table class="form-table">
|
390 |
+
<tr>
|
391 |
+
<th style="width:250px;"><?php $this->_e( 'Solving problems' ) ?>
|
392 |
+
<?php echo $this->tooltip_help( 'Some options to try when a problem occurs. These options can also cause other problems, so be carefull.' ) ?></th>
|
393 |
+
<td><label><?php echo $this->form->checkbox( 'fix_js', 1 ); ?>
|
394 |
+
<span><?php $this->_e( 'Replacing <code></a></code> with <code><\/a></code> in JavaScript code.' ) ?></span></label>
|
395 |
+
<?php echo $this->tooltip_help( 'By replacing </a> with <\/a> in JavaScript code these tags will not be processed by the plugin.' ) ?>
|
396 |
+
</td>
|
397 |
+
</tr>
|
398 |
+
<tr>
|
399 |
+
<th style="width:250px;"><?php $this->_e( 'Use phpQuery library' ) ?>
|
400 |
+
<?php echo $this->tooltip_help( 'Using phpQuery library for manipulating links. This option is experimental.' ) ?></th>
|
401 |
+
<td><label><?php echo $this->form->checkbox( 'phpquery', 1 ); ?>
|
402 |
+
<span><?php $this->_e( 'Use phpQuery for parsing document.' ) ?></span>
|
403 |
+
<span class="description">(Test it first!)</span></label>
|
404 |
+
</td>
|
405 |
+
</tr>
|
406 |
+
<tr class="filter_excl_sel" <?php echo ( $this->form->value( 'phpquery' ) ) ? '' : 'style="display:none;"'; ?>>
|
407 |
+
<th><?php $this->_e( 'Do NOT apply settings on...' ) ?>
|
408 |
+
<?php echo $this->tooltip_help( 'The external links of these selection will be excluded for the settings of this plugin. Define the selection by using CSS selectors.' ) ?></th>
|
409 |
+
<td><label><?php echo $this->form->textarea( 'filter_excl_sel' ); ?>
|
410 |
+
<span class="description"><?php _e( 'Define selection by using CSS selectors, f.e.: <code>.excl-ext-link, .entry-title, #comments-title</code> (look <a href="http://code.google.com/p/phpquery/wiki/Selectors" target="_blank">here</a> for available selectors).' ) ?></span></label>
|
411 |
+
</td>
|
412 |
+
</tr>
|
413 |
+
</table>
|
414 |
+
</fieldset>
|
415 |
+
<?php
|
416 |
+
echo $this->form->submit();
|
417 |
+
echo $this->form->close_form();
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* Meta Box: Extra Settings
|
422 |
+
*/
|
423 |
+
public function call_box_admin_settings() {
|
424 |
+
echo $this->form->set_current_option( 'screen' )->open_form();
|
425 |
+
?>
|
426 |
+
<fieldset class="options">
|
427 |
+
<table class="form-table">
|
428 |
+
<tr>
|
429 |
+
<th><?php $this->_e('Admin menu position') ?>
|
430 |
+
<?php echo $this->tooltip_help( 'Change the menu position of this plugin in "Screen Options".' ) ?></th>
|
431 |
+
<td><label>
|
432 |
+
<?php
|
433 |
+
echo $this->form->select( 'menu_position', array(
|
434 |
+
'admin.php' => 'Main menu',
|
435 |
+
'index.php' => $this->__( 'Subitem of Dashboard' ),
|
436 |
+
'edit.php' => $this->__( 'Subitem of Posts' ),
|
437 |
+
'upload.php' => $this->__( 'Subitem of Media' ),
|
438 |
+
'link-manager.php' => $this->__( 'Subitem of Links' ),
|
439 |
+
'edit.php?post_type=page' => $this->__( 'Subitem of Pages' ),
|
440 |
+
'edit-comments.php' => $this->__( 'Subitem of Comments' ),
|
441 |
+
'themes.php' => $this->__( 'Subitem of Appearance' ),
|
442 |
+
'plugins.php' => $this->__( 'Subitem of Plugins' ),
|
443 |
+
'users.php' => $this->__( 'Subitem of Users' ),
|
444 |
+
'tools.php' => $this->__( 'Subitem of Tools' ),
|
445 |
+
'options-general.php' => $this->__( 'Subitem of Settings' ),
|
446 |
+
));
|
447 |
+
?>
|
448 |
+
</label></td>
|
449 |
+
</tr>
|
450 |
+
</table>
|
451 |
+
</fieldset>
|
452 |
+
<?php
|
453 |
+
echo $this->form->submit();
|
454 |
+
echo $this->form->close_form();
|
455 |
+
}
|
456 |
+
|
457 |
+
/**
|
458 |
+
* Meta Box: About...
|
459 |
+
*/
|
460 |
+
public function call_box_about() {
|
461 |
+
?>
|
462 |
+
<h4><img src="<?php echo plugins_url( 'images/icon-wp-external-links-16.png', WP_EXTERNAL_LINKS_FILE ) ?>" width="16" height="16" /> <?php $this->_e( 'WP External Links' ) ?></h4>
|
463 |
+
<div>
|
464 |
+
<p><?php printf( $this->__( 'Current version: <strong>%1$s</strong>' ), WP_EXTERNAL_LINKS_VERSION ) ?></p>
|
465 |
+
<p><?php $this->_e( 'Manage external links on your site: open in new window/tab, set link icon, add "external", add "nofollow" and more.' ) ?></p>
|
466 |
+
<p><a href="http://www.freelancephp.net/contact/" target="_blank"><?php $this->_e( 'Questions or suggestions?' ) ?></a></p>
|
467 |
+
<p><?php $this->_e( 'If you like this plugin please send your rating at WordPress.org.' ) ?></p>
|
468 |
+
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-external-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-external-links-plugin/" target="_blank">FreelancePHP.net</a></p>
|
469 |
+
</div>
|
470 |
+
<?php
|
471 |
+
}
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Meta Box: Other Plugins
|
475 |
+
*/
|
476 |
+
public function call_box_other_plugins() {
|
477 |
+
?>
|
478 |
+
<h4><img src="<?php echo plugins_url( 'images/icon-email-encoder-bundle-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> Email Encoder Bundle</h4>
|
479 |
+
<div>
|
480 |
+
<?php if ( is_plugin_active( 'email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
|
481 |
+
<p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=email-encoder-bundle/email-encoder-bundle.php"><?php $this->_e( 'Settings' ) ?></a></p>
|
482 |
+
<?php elseif( file_exists( WP_PLUGIN_DIR . '/email-encoder-bundle/email-encoder-bundle.php' ) ): ?>
|
483 |
+
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
|
484 |
+
<?php else: ?>
|
485 |
+
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=Email+Encoder+Bundle+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
|
486 |
+
<?php endif; ?>
|
487 |
+
|
488 |
+
<p><?php $this->_e( 'Protect email addresses on your site from spambots and being used for spamming by using one of the encoding methods.' ) ?></p>
|
489 |
+
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/email-encoder-bundle/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/email-encoder-php-class-wp-plugin/" target="_blank">FreelancePHP.net</a></p>
|
490 |
+
</div>
|
491 |
+
|
492 |
+
<?php echo $this->hr(); ?>
|
493 |
+
|
494 |
+
<h4><img src="<?php echo plugins_url( 'images/icon-wp-mailto-links-16.png', WP_EXTERNAL_LINKS_FILE ); ?>" width="16" height="16" /> WP Mailto Links</h4>
|
495 |
+
<div>
|
496 |
+
<?php if ( is_plugin_active( 'wp-mailto-links/wp-mailto-links.php' ) ): ?>
|
497 |
+
<p><?php $this->_e( 'This plugin is already activated.' ) ?> <a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/options-general.php?page=wp-mailto-links/wp-mailto-links.php"><?php $this->_e( 'Settings' ) ?></a></p>
|
498 |
+
<?php elseif( file_exists( WP_PLUGIN_DIR . '/wp-mailto-links/wp-mailto-links.php' ) ): ?>
|
499 |
+
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugins.php?plugin_status=inactive"><?php $this->_e( 'Activate this plugin.' ) ?></a></p>
|
500 |
+
<?php else: ?>
|
501 |
+
<p><a href="<?php echo get_bloginfo( 'url' ) ?>/wp-admin/plugin-install.php?tab=search&type=term&s=WP+Mailto+Links+freelancephp&plugin-search-input=Search+Plugins"><?php $this->_e( 'Get this plugin now' ) ?></a></p>
|
502 |
+
<?php endif; ?>
|
503 |
+
|
504 |
+
<p><?php $this->_e( 'Manage mailto links on your site and protect emails from spambots, set mail icon and more.' ) ?></p>
|
505 |
+
<p><?php _e( 'More info' ) ?>: <a href="http://wordpress.org/extend/plugins/wp-mailto-links/" target="_blank">WordPress.org</a> | <a href="http://www.freelancephp.net/wp-mailto-links-plugin/" target="_blank">FreelancePHP.net</a></p>
|
506 |
+
</div>
|
507 |
+
<?php
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* Activation callback
|
512 |
+
*/
|
513 |
+
public function check_version_update() {
|
514 |
+
// check for version
|
515 |
+
$meta = get_option( 'wp_external_links-meta' );
|
516 |
+
if ( $meta[ 'version' ] == WP_EXTERNAL_LINKS_VERSION )
|
517 |
+
return;
|
518 |
+
|
519 |
+
// set new version
|
520 |
+
$meta[ 'version' ] = WP_EXTERNAL_LINKS_VERSION;
|
521 |
+
update_option( 'wp_external_links-meta', $meta );
|
522 |
+
$this->save_options['meta'] = $meta;
|
523 |
+
|
524 |
+
// check for upgrading saved options to v1.00
|
525 |
+
$old_options = get_option( 'WP_External_Links_options' );
|
526 |
+
|
527 |
+
if ( ! empty( $old_options ) ) {
|
528 |
+
$new_options = $this->save_options;
|
529 |
+
|
530 |
+
$new_options[ 'main' ][ 'target' ] = $old_options[ 'target' ];
|
531 |
+
$new_options[ 'main' ][ 'filter_page' ] = $old_options[ 'filter_whole_page' ];
|
532 |
+
$new_options[ 'main' ][ 'filter_posts' ] = $old_options[ 'filter_posts' ];
|
533 |
+
$new_options[ 'main' ][ 'filter_comments' ] = $old_options[ 'filter_comments' ];
|
534 |
+
$new_options[ 'main' ][ 'filter_widgets' ] = $old_options[ 'filter_widgets' ];
|
535 |
+
$new_options[ 'seo' ][ 'external' ] = $old_options[ 'external' ];
|
536 |
+
$new_options[ 'seo' ][ 'nofollow' ] = $old_options[ 'nofollow' ];
|
537 |
+
$new_options[ 'seo' ][ 'use_js' ] = $old_options[ 'use_js' ];
|
538 |
+
$new_options[ 'style' ][ 'class_name' ] = $old_options[ 'class_name' ];
|
539 |
+
$new_options[ 'style' ][ 'icon' ] = $old_options[ 'icon' ];
|
540 |
+
$new_options[ 'style' ][ 'no_icon_class' ] = $old_options[ 'no_icon_class' ];
|
541 |
+
$new_options[ 'style' ][ 'no_icon_same_window' ] = $old_options[ 'no_icon_same_window' ];
|
542 |
+
|
543 |
+
// save new format option values
|
544 |
+
update_option( 'wp_external_links-main', $new_options[ 'main' ] );
|
545 |
+
update_option( 'wp_external_links-seo', $new_options[ 'seo' ] );
|
546 |
+
update_option( 'wp_external_links-style', $new_options[ 'style' ] );
|
547 |
+
|
548 |
+
// delete old format option values
|
549 |
+
delete_option( 'WP_External_Links_options' );
|
550 |
+
}
|
551 |
+
|
552 |
+
// upgrade to v1.20
|
553 |
+
// $upgrade_main = get_option( 'wp_external_links-main' );
|
554 |
+
//
|
555 |
+
// if ( ! isset( $upgrade_main[ 'ignore' ] ) ) {
|
556 |
+
// $upgrade_main[ 'ignore' ] = $this->save_options[ 'main' ][ 'ignore' ];
|
557 |
+
// update_option( 'wp_external_links-main', $upgrade_main );
|
558 |
+
// }
|
559 |
+
|
560 |
+
// upgrade to v1.30
|
561 |
+
if ( WP_EXTERNAL_LINKS_VERSION == '1.30' ) {
|
562 |
+
$new_options = $this->save_options;
|
563 |
+
$general = get_option( 'wp_external_links-general' );
|
564 |
+
$style = get_option( 'wp_external_links-style' );
|
565 |
+
|
566 |
+
if ( isset( $general[ 'target' ] ) ) $new_options[ 'main' ][ 'target' ] = $general[ 'target' ];
|
567 |
+
$new_options[ 'main' ][ 'filter_page' ] = ( isset( $general[ 'filter_page' ] ) ) ? $general[ 'filter_page' ] : 0;
|
568 |
+
$new_options[ 'main' ][ 'filter_posts' ] = ( isset( $general[ 'filter_posts' ] ) ) ? $general[ 'filter_posts' ] : 0;
|
569 |
+
$new_options[ 'main' ][ 'filter_comments' ] = ( isset( $general[ 'filter_comments' ] ) ) ? $general[ 'filter_comments' ] : 0;
|
570 |
+
$new_options[ 'main' ][ 'filter_widgets' ] = ( isset( $general[ 'filter_widgets' ] ) ) ? $general[ 'filter_widgets' ] : 0;
|
571 |
+
if ( isset( $general[ 'ignore' ] ) ) $new_options[ 'main' ][ 'ignore' ] = $general[ 'ignore' ];
|
572 |
+
|
573 |
+
$new_options[ 'seo' ][ 'external' ] = ( isset( $general[ 'external' ] ) ) ? $general[ 'external' ] : 0;
|
574 |
+
$new_options[ 'seo' ][ 'nofollow' ] = ( isset( $general[ 'nofollow' ] ) ) ? $general[ 'nofollow' ] : 0;
|
575 |
+
$new_options[ 'seo' ][ 'use_js' ] = ( isset( $general[ 'use_js' ] ) ) ? $general[ 'use_js' ] : 0;
|
576 |
+
if ( isset( $general[ 'title' ] ) ) $new_options[ 'seo' ][ 'title' ] = $general[ 'title' ];
|
577 |
+
|
578 |
+
if ( isset( $general[ 'class_name' ] ) ) $new_options[ 'style' ][ 'class_name' ] = $general[ 'class_name' ];
|
579 |
+
|
580 |
+
if ( isset( $style[ 'icon' ] ) ) $new_options[ 'style' ][ 'icon' ] = $style[ 'icon' ];
|
581 |
+
if ( isset( $style[ 'no_icon_class' ] ) ) $new_options[ 'style' ][ 'no_icon_class' ] = $style[ 'no_icon_class' ];
|
582 |
+
$new_options[ 'style' ][ 'no_icon_same_window' ] = ( isset( $style[ 'no_icon_same_window' ] ) ) ? $style[ 'no_icon_same_window' ] : 0;
|
583 |
+
|
584 |
+
$new_options[ 'extra' ][ 'fix_js' ] = ( isset( $general[ 'fix_js' ] ) ) ? $general[ 'fix_js' ] : 0;
|
585 |
+
$new_options[ 'extra' ][ 'phpquery' ] = ( isset( $general[ 'phpquery' ] ) ) ? $general[ 'phpquery' ] : 0;
|
586 |
+
if ( isset( $general[ 'filter_excl_sel' ] ) ) $new_options[ 'extra' ][ 'filter_excl_sel' ] = $general[ 'filter_excl_sel' ];
|
587 |
+
|
588 |
+
// save new format option values
|
589 |
+
update_option( 'wp_external_links-main', $new_options[ 'main' ] );
|
590 |
+
update_option( 'wp_external_links-seo', $new_options[ 'seo' ] );
|
591 |
+
update_option( 'wp_external_links-style', $new_options[ 'style' ] );
|
592 |
+
update_option( 'wp_external_links-extra', $new_options[ 'extra' ] );
|
593 |
+
|
594 |
+
// delete old format
|
595 |
+
delete_option( 'wp_external_links-general' );
|
596 |
+
}
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Method for test purpuses
|
601 |
+
*/
|
602 |
+
public function __options($values = null) {
|
603 |
+
if (class_exists('Test_WP_Mailto_Links') && constant('WP_DEBUG') === true) {
|
604 |
+
if ($values !== null) {
|
605 |
+
$this->set_options($values);
|
606 |
+
}
|
607 |
+
|
608 |
+
return $this->options;
|
609 |
+
}
|
610 |
+
}
|
611 |
+
|
612 |
+
/**
|
613 |
+
* Uninstall callback
|
614 |
+
*/
|
615 |
+
static public function call_uninstall() {
|
616 |
+
self::$staticForm->delete_options();
|
617 |
+
}
|
618 |
+
|
619 |
+
/**
|
620 |
+
* Set tooltip help
|
621 |
+
* @param string $text
|
622 |
+
* @return string
|
623 |
+
*/
|
624 |
+
public function tooltip_help( $text ) {
|
625 |
+
$text = $this->__( $text );
|
626 |
+
$text = htmlentities( $text );
|
627 |
+
|
628 |
+
$html = '<a href="#" class="tooltip-help" title="'. $text .'"><sup>(?)</sup></a>';
|
629 |
+
return $html;
|
630 |
+
}
|
631 |
+
|
632 |
+
/**
|
633 |
+
* Get html seperator
|
634 |
+
* @return string
|
635 |
+
*/
|
636 |
+
protected function hr() {
|
637 |
+
return '<hr style="border:1px solid #FFF; border-top:1px solid #EEE;" />';
|
638 |
+
}
|
639 |
+
|
640 |
+
|
641 |
+
/**
|
642 |
+
* Check if widget_content filter is available (Widget Logic Plugin)
|
643 |
+
* @return boolean
|
644 |
+
* @static
|
645 |
+
*/
|
646 |
+
public static function check_widget_content_filter() {
|
647 |
+
// set widget_content filter of Widget Logic plugin
|
648 |
+
$widget_logic_opts = get_option( 'widget_logic' );
|
649 |
+
|
650 |
+
if ( function_exists( 'widget_logic_expand_control' ) AND is_array( $widget_logic_opts ) AND key_exists( 'widget_logic-options-filter', $widget_logic_opts ) )
|
651 |
+
return ( $widget_logic_opts[ 'widget_logic-options-filter' ] == 'checked' );
|
652 |
+
|
653 |
+
return FALSE;
|
654 |
+
}
|
655 |
+
|
656 |
+
} // End Admin_External_Links Class
|
657 |
+
|
658 |
+
endif;
|
659 |
+
|
660 |
+
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
includes/class-wp-external-links.php
CHANGED
@@ -1,607 +1,607 @@
|
|
1 |
-
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
-
if ( ! class_exists( 'WP_External_Links' ) ):
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Class WP_External_Links
|
6 |
-
* @package WordPress
|
7 |
-
* @since
|
8 |
-
* @category WordPress Plugins
|
9 |
-
*/
|
10 |
-
final class WP_External_Links {
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Admin object
|
14 |
-
* @var Admin_External_Links
|
15 |
-
*/
|
16 |
-
public $admin = NULL;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Array of ignored links
|
20 |
-
* @var type
|
21 |
-
*/
|
22 |
-
private $ignored = array();
|
23 |
-
|
24 |
-
|
25 |
-
/**
|
26 |
-
* Constructor
|
27 |
-
*/
|
28 |
-
public function __construct() {
|
29 |
-
// set admin object
|
30 |
-
$this->admin = new Admin_External_Links();
|
31 |
-
|
32 |
-
// add actions
|
33 |
-
add_action( 'wp', array( $this, 'call_wp' ) );
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Quick helper method for getting saved option values
|
38 |
-
* @param string $key
|
39 |
-
* @return mixed
|
40 |
-
*/
|
41 |
-
public function get_opt( $key ) {
|
42 |
-
$lookup = $this->admin->save_options;
|
43 |
-
|
44 |
-
foreach ( $lookup as $option_name => $values ) {
|
45 |
-
$value = $this->admin->form->value( $key, '___NONE___', $option_name );
|
46 |
-
|
47 |
-
if ($value !== '___NONE___')
|
48 |
-
return $value;
|
49 |
-
}
|
50 |
-
|
51 |
-
throw new Exception('Option with key "' . $key . '" does not exist.');
|
52 |
-
}
|
53 |
-
|
54 |
-
/**
|
55 |
-
* wp callback
|
56 |
-
*/
|
57 |
-
public function call_wp() {
|
58 |
-
if ( ! is_admin() && ! is_feed() ) {
|
59 |
-
// add wp_head for setting js vars and css style
|
60 |
-
add_action( 'wp_head', array( $this, 'call_wp_head' ) );
|
61 |
-
|
62 |
-
// set js file
|
63 |
-
if ( $this->get_opt( 'use_js' ) )
|
64 |
-
wp_enqueue_script( 'wp-external-links', plugins_url( 'js/wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array(), WP_EXTERNAL_LINKS_VERSION, (bool) $this->get_opt( 'load_in_footer' ) );
|
65 |
-
|
66 |
-
// set ignored
|
67 |
-
$ignored = $this->get_opt( 'ignore' );
|
68 |
-
$ignored = trim( $ignored );
|
69 |
-
$ignored = explode( "\n", $ignored );
|
70 |
-
$ignored = array_map( 'trim', $ignored );
|
71 |
-
$ignored = array_map( 'strtolower', $ignored );
|
72 |
-
$this->ignored = $ignored;
|
73 |
-
|
74 |
-
// filters
|
75 |
-
if ( $this->get_opt( 'filter_page' ) ) {
|
76 |
-
// filter body
|
77 |
-
ob_start( array( $this, 'call_filter_content' ) );
|
78 |
-
|
79 |
-
// set ob flush
|
80 |
-
add_action('wp_footer', array($this, 'callback_flush_buffer'), 10000);
|
81 |
-
|
82 |
-
} else {
|
83 |
-
// set filter priority
|
84 |
-
$priority = 1000000000;
|
85 |
-
|
86 |
-
// content
|
87 |
-
if ( $this->get_opt( 'filter_posts' ) ) {
|
88 |
-
add_filter( 'the_title', array( $this, 'call_filter_content' ), $priority );
|
89 |
-
add_filter( 'the_content', array( $this, 'call_filter_content' ), $priority );
|
90 |
-
add_filter( 'get_the_excerpt', array( $this, 'call_filter_content' ), $priority );
|
91 |
-
// redundant:
|
92 |
-
//add_filter( 'the_excerpt', array( $this, 'call_filter_content' ), $priority );
|
93 |
-
}
|
94 |
-
|
95 |
-
// comments
|
96 |
-
if ( $this->get_opt( 'filter_comments' ) ) {
|
97 |
-
add_filter( 'get_comment_text', array( $this, 'call_filter_content' ), $priority );
|
98 |
-
// redundant:
|
99 |
-
//add_filter( 'comment_text', array( $this, 'call_filter_content' ), $priority );
|
100 |
-
|
101 |
-
add_filter( 'comment_excerpt', array( $this, 'call_filter_content' ), $priority );
|
102 |
-
// redundant:
|
103 |
-
//add_filter( 'get_comment_excerpt', array( $this, 'call_filter_content' ), $priority );
|
104 |
-
|
105 |
-
add_filter( 'comment_url', array( $this, 'call_filter_content' ), $priority );
|
106 |
-
add_filter( 'get_comment_author_url', array( $this, 'call_filter_content' ), $priority );
|
107 |
-
add_filter( 'get_comment_author_link', array( $this, 'call_filter_content' ), $priority );
|
108 |
-
add_filter( 'get_comment_author_url_link', array( $this, 'call_filter_content' ), $priority );
|
109 |
-
}
|
110 |
-
|
111 |
-
// widgets
|
112 |
-
if ( $this->get_opt( 'filter_widgets' ) ) {
|
113 |
-
if ( $this->admin->check_widget_content_filter() ) {
|
114 |
-
// only if Widget Logic plugin is installed and 'widget_content' option is activated
|
115 |
-
add_filter( 'widget_content', array( $this, 'call_filter_content' ), $priority );
|
116 |
-
} else {
|
117 |
-
// filter text widgets
|
118 |
-
add_filter( 'widget_title', array( $this, 'call_filter_content' ), $priority );
|
119 |
-
add_filter( 'widget_text', array( $this, 'call_filter_content' ), $priority );
|
120 |
-
}
|
121 |
-
}
|
122 |
-
}
|
123 |
-
}
|
124 |
-
|
125 |
-
// hook
|
126 |
-
do_action('wpel_ready', array($this, 'call_filter_content'), $this);
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* End output buffer
|
131 |
-
*/
|
132 |
-
public function callback_flush_buffer() {
|
133 |
-
ob_end_flush();
|
134 |
-
}
|
135 |
-
|
136 |
-
/**
|
137 |
-
* wp_head callback
|
138 |
-
*/
|
139 |
-
public function call_wp_head() {
|
140 |
-
$icon = $this->get_opt('icon');
|
141 |
-
|
142 |
-
if ($icon) {
|
143 |
-
$padding = ($icon < 20) ? 15 : 12;
|
144 |
-
?>
|
145 |
-
<style type="text/css" media="screen">
|
146 |
-
/* WP External Links Plugin */
|
147 |
-
.ext-icon-<?php echo $icon ?> { background:url(<?php echo plugins_url('/images/ext-icons/ext-icon-' . $icon . '.png', WP_EXTERNAL_LINKS_FILE) ?>) no-repeat 100% 50%; padding-right:<?php echo $padding ?>px; }';
|
148 |
-
</style>
|
149 |
-
<?php
|
150 |
-
}
|
151 |
-
}
|
152 |
-
|
153 |
-
/**
|
154 |
-
* Filter content
|
155 |
-
* @param string $content
|
156 |
-
* @return string
|
157 |
-
*/
|
158 |
-
public function call_filter_content( $content ) {
|
159 |
-
if ( $this->get_opt( 'fix_js' ) ) {
|
160 |
-
// fix js problem by replacing </a> by <\/a>
|
161 |
-
$content = preg_replace_callback( '/<script([^>]*)>(.*?)<\/script[^>]*>/is', array( $this, 'call_fix_js' ), $content );
|
162 |
-
}
|
163 |
-
|
164 |
-
if ( $this->get_opt( 'phpquery' ) ) {
|
165 |
-
// Include phpQuery
|
166 |
-
if ( ! class_exists( 'phpQuery' ) ) {
|
167 |
-
require_once( 'phpQuery.php' );
|
168 |
-
}
|
169 |
-
|
170 |
-
return $this->filter_phpquery( $content );
|
171 |
-
} else {
|
172 |
-
return $this->filter( $content );
|
173 |
-
}
|
174 |
-
}
|
175 |
-
|
176 |
-
/**
|
177 |
-
* Fix </a> in JavaScript blocks (callback for regexp)
|
178 |
-
* @param array $matches Result of a preg call in filter_content()
|
179 |
-
* @return string Clean code
|
180 |
-
*/
|
181 |
-
public function call_fix_js( $matches ) {
|
182 |
-
return str_replace( '</a>', '<\/a>', $matches[ 0 ] );
|
183 |
-
}
|
184 |
-
|
185 |
-
/**
|
186 |
-
* Check if link is external
|
187 |
-
* @param string $href
|
188 |
-
* @param string $rel
|
189 |
-
* @return boolean
|
190 |
-
*/
|
191 |
-
private function is_external( $href, $rel ) {
|
192 |
-
return ( isset( $href ) AND ( ( strpos( $href, strtolower( get_bloginfo( 'wpurl' ) ) ) === FALSE )
|
193 |
-
AND ( substr( $href, 0, 7 ) == 'http://'
|
194 |
-
OR substr( $href, 0, 8 ) == 'https://'
|
195 |
-
OR substr( $href, 0, 6 ) == 'ftp://'
|
196 |
-
OR substr( $href, 0, 2 ) == '//' ) ) );
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Is an ignored link
|
201 |
-
* @param string $href
|
202 |
-
* @return boolean
|
203 |
-
*/
|
204 |
-
private function is_ignored( $href ) {
|
205 |
-
// check if this links should be ignored
|
206 |
-
for ( $x = 0, $count = count($this->ignored); $x < $count; $x++ ) {
|
207 |
-
if ( strrpos( $href, $this->ignored[ $x ] ) !== FALSE )
|
208 |
-
return TRUE;
|
209 |
-
}
|
210 |
-
|
211 |
-
return FALSE;
|
212 |
-
}
|
213 |
-
|
214 |
-
/**
|
215 |
-
* Filter content
|
216 |
-
* @param string $content
|
217 |
-
* @return string
|
218 |
-
*/
|
219 |
-
private function filter( $content ) {
|
220 |
-
// replace links
|
221 |
-
$content = preg_replace_callback( '/<a[^A-Za-z](.*?)>(.*?)<\/a[\s+]*>/is', array( $this, 'call_parse_link' ), $content );
|
222 |
-
|
223 |
-
// remove style when no icon classes are found
|
224 |
-
if ( strpos( $content, 'ext-icon-' ) === FALSE ) {
|
225 |
-
// remove style with id wp-external-links-css
|
226 |
-
$content = preg_replace( '/<link ([^>]*)wp-external-links-css([^>]*)\/>[\s+]*/i', '', $content );
|
227 |
-
}
|
228 |
-
|
229 |
-
return $content;
|
230 |
-
}
|
231 |
-
|
232 |
-
/**
|
233 |
-
* Parse an attributes string into an array. If the string starts with a tag,
|
234 |
-
* then the attributes on the first tag are parsed. This parses via a manual
|
235 |
-
* loop and is designed to be safer than using DOMDocument.
|
236 |
-
*
|
237 |
-
* @param string|* $attrs
|
238 |
-
* @return array
|
239 |
-
*
|
240 |
-
* @example parse_attrs( 'src="example.jpg" alt="example"' )
|
241 |
-
* @example parse_attrs( '<img src="example.jpg" alt="example">' )
|
242 |
-
* @example parse_attrs( '<a href="example"></a>' )
|
243 |
-
* @example parse_attrs( '<a href="example">' )
|
244 |
-
*
|
245 |
-
* @link http://dev.airve.com/demo/speed_tests/php/parse_attrs.php
|
246 |
-
*/
|
247 |
-
private function parse_attrs ($attrs) {
|
248 |
-
if ( ! is_scalar($attrs) )
|
249 |
-
return (array) $attrs;
|
250 |
-
|
251 |
-
$attrs = str_split( trim($attrs) );
|
252 |
-
|
253 |
-
if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
|
254 |
-
while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
|
255 |
-
array_shift($attrs);
|
256 |
-
|
257 |
-
$arr = array(); # output
|
258 |
-
$name = ''; # for the current attr being parsed
|
259 |
-
$value = ''; # for the current attr being parsed
|
260 |
-
$mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
|
261 |
-
$stop = false; # delimiter for the current $value being parsed
|
262 |
-
$space = ' '; # a single space
|
263 |
-
|
264 |
-
foreach ( $attrs as $j => $curr ) {
|
265 |
-
if ( $mode < 0 ) {# name
|
266 |
-
if ( '=' === $curr ) {
|
267 |
-
$mode = 1;
|
268 |
-
$stop = false;
|
269 |
-
} elseif ( '>' === $curr ) {
|
270 |
-
'' === $name or $arr[ $name ] = $value;
|
271 |
-
break;
|
272 |
-
} elseif ( ! ctype_space($curr) ) {
|
273 |
-
if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
|
274 |
-
'' === $name or $arr[ $name ] = ''; # previous name
|
275 |
-
$name = $curr; # initiate new
|
276 |
-
} else {
|
277 |
-
$name .= $curr;
|
278 |
-
}
|
279 |
-
}
|
280 |
-
} elseif ( $mode > 0 ) {# value
|
281 |
-
|
282 |
-
if ( $stop === false ) {
|
283 |
-
if ( ! ctype_space($curr) ) {
|
284 |
-
if ( '"' === $curr || "'" === $curr ) {
|
285 |
-
$value = '';
|
286 |
-
$stop = $curr;
|
287 |
-
} else {
|
288 |
-
$value = $curr;
|
289 |
-
$stop = $space;
|
290 |
-
}
|
291 |
-
}
|
292 |
-
} elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
|
293 |
-
$arr[ $name ] = $value;
|
294 |
-
$mode = 0;
|
295 |
-
$name = $value = '';
|
296 |
-
} else {
|
297 |
-
$value .= $curr;
|
298 |
-
}
|
299 |
-
} else {# neither
|
300 |
-
|
301 |
-
if ( '>' === $curr )
|
302 |
-
break;
|
303 |
-
if ( ! ctype_space( $curr ) ) {
|
304 |
-
# initiate
|
305 |
-
$name = $curr;
|
306 |
-
$mode = -1;
|
307 |
-
}
|
308 |
-
}
|
309 |
-
}
|
310 |
-
|
311 |
-
# incl the final pair if it was quoteless
|
312 |
-
'' === $name or $arr[ $name ] = $value;
|
313 |
-
|
314 |
-
return $arr;
|
315 |
-
}
|
316 |
-
|
317 |
-
/**
|
318 |
-
* Make a clean <a> code (callback for regexp)
|
319 |
-
* @param array $matches Result of a preg call in filter_content()
|
320 |
-
* @return string Clean <a> code
|
321 |
-
*/
|
322 |
-
public function call_parse_link( $matches ) {
|
323 |
-
$link = $matches[ 0 ];
|
324 |
-
$label = $matches[ 2 ];
|
325 |
-
$created_link = $link;
|
326 |
-
|
327 |
-
// parse attributes
|
328 |
-
$attrs = $matches[ 1 ];
|
329 |
-
$attrs = stripslashes( $attrs );
|
330 |
-
$attrs = $this->parse_attrs( $attrs );
|
331 |
-
|
332 |
-
$rel = ( isset( $attrs[ 'rel' ] ) ) ? strtolower( $attrs[ 'rel' ] ) : '';
|
333 |
-
|
334 |
-
// href preperation
|
335 |
-
if (isset( $attrs[ 'href' ])) {
|
336 |
-
$href = $attrs[ 'href' ];
|
337 |
-
$href = strtolower( $href );
|
338 |
-
$href = trim( $href );
|
339 |
-
} else {
|
340 |
-
$href = '';
|
341 |
-
}
|
342 |
-
|
343 |
-
// checks
|
344 |
-
$is_external = $this->is_external( $href, $rel );
|
345 |
-
$is_ignored = $this->is_ignored( $href );
|
346 |
-
$has_rel_external = (strpos( $rel, 'external' ) !== FALSE);
|
347 |
-
|
348 |
-
// is an internal link?
|
349 |
-
// rel=external will be threaded as external link
|
350 |
-
if ( ! $is_external && ! $has_rel_external) {
|
351 |
-
return apply_filters('wpel_internal_link', $created_link, $label, $attrs);
|
352 |
-
}
|
353 |
-
|
354 |
-
// is an ignored link?
|
355 |
-
// rel=external will be threaded as external link
|
356 |
-
if ( $is_ignored && ! $has_rel_external ) {
|
357 |
-
return apply_filters('wpel_external_link', $created_link, $link, $label, $attrs, TRUE);
|
358 |
-
}
|
359 |
-
|
360 |
-
// set rel="external" (when not already set)
|
361 |
-
if ( $this->get_opt( 'external' ) )
|
362 |
-
$this->add_attr_value( $attrs, 'rel', 'external' );
|
363 |
-
|
364 |
-
// set rel="nofollow" when doesn't have "follow" (or already "nofollow")
|
365 |
-
if ( $this->get_opt( 'nofollow' ) AND strpos( $rel, 'follow' ) === FALSE )
|
366 |
-
$this->add_attr_value( $attrs, 'rel', 'nofollow' );
|
367 |
-
|
368 |
-
// set title
|
369 |
-
$title_format = $this->get_opt( 'title' );
|
370 |
-
$title = ( isset( $attrs[ 'title' ] ) ) ? $attrs[ 'title' ] : '';
|
371 |
-
$attrs[ 'title' ] = str_replace( '%title%', $title, $title_format );
|
372 |
-
|
373 |
-
// set user-defined class
|
374 |
-
$class = $this->get_opt( 'class_name' );
|
375 |
-
if ( $class )
|
376 |
-
$this->add_attr_value( $attrs, 'class', $class );
|
377 |
-
|
378 |
-
// set icon class, unless no-icon class isset or another icon class ('ext-icon-...') is found or content contains image
|
379 |
-
if ( $this->get_opt( 'icon' ) > 0
|
380 |
-
AND ( ! $this->get_opt( 'no_icon_class' ) OR strpos( $attrs[ 'class' ], $this->get_opt( 'no_icon_class' ) ) === FALSE )
|
381 |
-
AND strpos( $attrs[ 'class' ], 'ext-icon-' ) === FALSE
|
382 |
-
AND !( $this->get_opt( 'image_no_icon' ) AND (bool) preg_match( '/<img([^>]*)>/is', $label )) ){
|
383 |
-
$icon_class = 'ext-icon-'. $this->get_opt( 'icon', 'style' );
|
384 |
-
$this->add_attr_value( $attrs, 'class', $icon_class );
|
385 |
-
}
|
386 |
-
|
387 |
-
// set target
|
388 |
-
$no_icon_class = $this->get_opt( 'no_icon_class' );
|
389 |
-
$target = $this->get_opt( 'target' );
|
390 |
-
|
391 |
-
// remove target
|
392 |
-
unset($attrs[ 'target' ]);
|
393 |
-
|
394 |
-
if ($this->get_opt( 'no_icon_same_window' )
|
395 |
-
AND $no_icon_class AND strpos( $attrs[ 'class' ], $no_icon_class ) !== FALSE) {
|
396 |
-
// open in same window
|
397 |
-
} elseif ($target && $target !== '_none') {
|
398 |
-
if ($this->get_opt( 'use_js' )) {
|
399 |
-
// add data-attr for javascript
|
400 |
-
$attrs['data-wpel-target'] = $target;
|
401 |
-
} else {
|
402 |
-
// set target value
|
403 |
-
$attrs[ 'target' ] = $target;
|
404 |
-
}
|
405 |
-
}
|
406 |
-
|
407 |
-
// create element code
|
408 |
-
$created_link = '<a';
|
409 |
-
|
410 |
-
foreach ( $attrs AS $key => $value ) {
|
411 |
-
$created_link .= ' '. $key .'="'. $value .'"';
|
412 |
-
}
|
413 |
-
|
414 |
-
$created_link .= '>'. $label .'</a>';
|
415 |
-
|
416 |
-
// filter
|
417 |
-
$created_link = apply_filters('wpel_external_link', $created_link, $link, $label, $attrs, FALSE);
|
418 |
-
|
419 |
-
return $created_link;
|
420 |
-
}
|
421 |
-
|
422 |
-
/**
|
423 |
-
* Add value to attribute
|
424 |
-
* @param array $attrs
|
425 |
-
* @param string $attr
|
426 |
-
* @param string $value
|
427 |
-
* @param string $default Optional, default NULL which means tje attribute will be removed when (new) value is empty
|
428 |
-
* @return New value
|
429 |
-
*/
|
430 |
-
public function add_attr_value( &$attrs, $attr_name, $value, $default = NULL ) {
|
431 |
-
if ( key_exists( $attr_name, $attrs ) )
|
432 |
-
$old_value = $attrs[ $attr_name ];
|
433 |
-
|
434 |
-
if ( empty( $old_value ) )
|
435 |
-
$old_value = '';
|
436 |
-
|
437 |
-
$split = explode( ' ', strtolower( $old_value ) );
|
438 |
-
|
439 |
-
if ( in_array( $value, $split ) ) {
|
440 |
-
$value = $old_value;
|
441 |
-
} else {
|
442 |
-
$value = ( empty( $old_value ) )
|
443 |
-
? $value
|
444 |
-
: $old_value .' '. $value;
|
445 |
-
}
|
446 |
-
|
447 |
-
if ( empty( $value ) AND $default === NULL ) {
|
448 |
-
unset( $attrs[ $attr_name ] );
|
449 |
-
} else {
|
450 |
-
$attrs[ $attr_name ] = $value;
|
451 |
-
}
|
452 |
-
|
453 |
-
return $value;
|
454 |
-
}
|
455 |
-
|
456 |
-
/**
|
457 |
-
* Experimental phpQuery...
|
458 |
-
*/
|
459 |
-
|
460 |
-
/**
|
461 |
-
* Filter content
|
462 |
-
* @param string $content
|
463 |
-
* @return string
|
464 |
-
*/
|
465 |
-
private function filter_phpquery( $content ) {
|
466 |
-
// Workaround: remove <head>-attributes before using phpQuery
|
467 |
-
$regexp_head = '/<head(>|\s(.*?)>)>/is';
|
468 |
-
$clean_head = '<head>';
|
469 |
-
|
470 |
-
// set simple <head> without attributes
|
471 |
-
preg_match( $regexp_head, $content, $matches );
|
472 |
-
|
473 |
-
if( count( $matches ) > 0 ) {
|
474 |
-
$original_head = $matches[ 0 ];
|
475 |
-
$content = str_replace( $original_head, $clean_head, $content );
|
476 |
-
}
|
477 |
-
|
478 |
-
//phpQuery::$debug = true;
|
479 |
-
|
480 |
-
// set document
|
481 |
-
$doc = phpQuery::newDocument( $content );
|
482 |
-
|
483 |
-
$excl_sel = $this->get_opt( 'filter_excl_sel' );
|
484 |
-
|
485 |
-
// set excludes
|
486 |
-
if ( ! empty( $excl_sel ) ) {
|
487 |
-
$excludes = $doc->find( $excl_sel );
|
488 |
-
$excludes->filter( 'a' )->attr( 'excluded', true );
|
489 |
-
$excludes->find( 'a' )->attr( 'excluded', true );
|
490 |
-
}
|
491 |
-
|
492 |
-
// get <a>-tags
|
493 |
-
$links = $doc->find( 'a' );
|
494 |
-
|
495 |
-
// set links
|
496 |
-
$count = count( $links );
|
497 |
-
|
498 |
-
for( $x = 0; $x < $count; $x++ ) {
|
499 |
-
$a = $links->eq( $x );
|
500 |
-
|
501 |
-
if ( ! $a->attr( 'excluded' ) )
|
502 |
-
$this->set_link_phpquery( $links->eq( $x ) );
|
503 |
-
}
|
504 |
-
|
505 |
-
// remove excluded
|
506 |
-
if ( ! empty( $excl_sel ) ) {
|
507 |
-
$excludes = $doc->find( $excl_sel );
|
508 |
-
$excludes->filter( 'a' )->removeAttr( 'excluded' );
|
509 |
-
$excludes->find( 'a' )->removeAttr( 'excluded' );
|
510 |
-
}
|
511 |
-
|
512 |
-
// remove style when no icon classes are found
|
513 |
-
if ( strpos( $doc, 'ext-icon-' ) === FALSE ) {
|
514 |
-
// remove icon css
|
515 |
-
$css = $doc->find( 'link#wp-external-links-css' )->eq(0);
|
516 |
-
$css->remove();
|
517 |
-
}
|
518 |
-
|
519 |
-
// get document content
|
520 |
-
$content = (string) $doc;
|
521 |
-
|
522 |
-
if( isset( $original_head ) ) {
|
523 |
-
// recover original <head> with attributes
|
524 |
-
$content = str_replace( $clean_head, $original_head, $content );
|
525 |
-
}
|
526 |
-
|
527 |
-
return $content;
|
528 |
-
}
|
529 |
-
|
530 |
-
/**
|
531 |
-
* Set link...
|
532 |
-
* @param Node $a
|
533 |
-
* @return Node
|
534 |
-
*/
|
535 |
-
public function set_link_phpquery( $a ) {
|
536 |
-
$href = strtolower( $a->attr( 'href' ) . '' );
|
537 |
-
$rel = strtolower( $a->attr( 'rel' ) . '' );
|
538 |
-
|
539 |
-
// check if it is an external link and not excluded
|
540 |
-
if ( ! $this->is_external( $href, $rel ) || $this->is_ignored( $href ) )
|
541 |
-
return $a;
|
542 |
-
|
543 |
-
// add "external" to rel-attribute
|
544 |
-
if ( $this->get_opt( 'external' ) ){
|
545 |
-
$this->add_attr_value_phpquery( $a, 'rel', 'external' );
|
546 |
-
}
|
547 |
-
|
548 |
-
// add "nofollow" to rel-attribute, when doesn't have "follow"
|
549 |
-
if ( $this->get_opt( 'nofollow' ) AND strpos( $rel, 'follow' ) === FALSE ){
|
550 |
-
$this->add_attr_value_phpquery( $a, 'rel', 'nofollow' );
|
551 |
-
}
|
552 |
-
|
553 |
-
// set title
|
554 |
-
$title = str_replace( '%title%', $a->attr( 'title' ), $this->get_opt( 'title' ) );
|
555 |
-
$a->attr( 'title', $title );
|
556 |
-
|
557 |
-
// add icon class, unless no-icon class isset or another icon class ('ext-icon-...') is found
|
558 |
-
if ( $this->get_opt( 'icon' ) > 0 AND ( ! $this->get_opt( 'no_icon_class' ) OR strpos( $a->attr( 'class' ), $this->get_opt( 'no_icon_class' ) ) === FALSE ) AND strpos( $a->attr( 'class' ), 'ext-icon-' ) === FALSE ){
|
559 |
-
$icon_class = 'ext-icon-'. $this->get_opt( 'icon' );
|
560 |
-
$a->addClass( $icon_class );
|
561 |
-
}
|
562 |
-
|
563 |
-
// add user-defined class
|
564 |
-
if ( $this->get_opt( 'class_name' ) ){
|
565 |
-
$a->addClass( $this->get_opt( 'class_name' ) );
|
566 |
-
}
|
567 |
-
|
568 |
-
// set target
|
569 |
-
if ( $this->get_opt( 'target' ) != '_none' AND ! $this->get_opt( 'use_js' ) AND ( ! $this->get_opt( 'no_icon_same_window' ) OR ! $this->get_opt( 'no_icon_class' ) OR strpos( $a->attr( 'class' ), $this->get_opt( 'no_icon_class' ) ) === FALSE ) )
|
570 |
-
$a->attr( 'target', $this->get_opt( 'target' ) );
|
571 |
-
|
572 |
-
return $a;
|
573 |
-
}
|
574 |
-
|
575 |
-
/**
|
576 |
-
* Add value to attribute
|
577 |
-
* @param Node $node
|
578 |
-
* @param string $attr
|
579 |
-
* @param string $value
|
580 |
-
* @return New value
|
581 |
-
*/
|
582 |
-
private function add_attr_value_phpquery( $node, $attr, $value ) {
|
583 |
-
$old_value = $node->attr( $attr );
|
584 |
-
|
585 |
-
if ( empty( $old_value ) )
|
586 |
-
$old_value = '';
|
587 |
-
|
588 |
-
$split = split( ' ', strtolower( $old_value ) );
|
589 |
-
|
590 |
-
if ( in_array( $value, $split ) ) {
|
591 |
-
$value = $old_value;
|
592 |
-
} else {
|
593 |
-
$value = ( empty( $old_value ) )
|
594 |
-
? $value
|
595 |
-
: $old_value .' '. $value;
|
596 |
-
}
|
597 |
-
|
598 |
-
$node->attr( $attr, $value );
|
599 |
-
|
600 |
-
return $value;
|
601 |
-
}
|
602 |
-
|
603 |
-
} // End WP_External_Links Class
|
604 |
-
|
605 |
-
endif;
|
606 |
-
|
607 |
-
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
1 |
+
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
+
if ( ! class_exists( 'WP_External_Links' ) ):
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class WP_External_Links
|
6 |
+
* @package WordPress
|
7 |
+
* @since
|
8 |
+
* @category WordPress Plugins
|
9 |
+
*/
|
10 |
+
final class WP_External_Links {
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Admin object
|
14 |
+
* @var Admin_External_Links
|
15 |
+
*/
|
16 |
+
public $admin = NULL;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Array of ignored links
|
20 |
+
* @var type
|
21 |
+
*/
|
22 |
+
private $ignored = array();
|
23 |
+
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Constructor
|
27 |
+
*/
|
28 |
+
public function __construct() {
|
29 |
+
// set admin object
|
30 |
+
$this->admin = new Admin_External_Links();
|
31 |
+
|
32 |
+
// add actions
|
33 |
+
add_action( 'wp', array( $this, 'call_wp' ) );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Quick helper method for getting saved option values
|
38 |
+
* @param string $key
|
39 |
+
* @return mixed
|
40 |
+
*/
|
41 |
+
public function get_opt( $key ) {
|
42 |
+
$lookup = $this->admin->save_options;
|
43 |
+
|
44 |
+
foreach ( $lookup as $option_name => $values ) {
|
45 |
+
$value = $this->admin->form->value( $key, '___NONE___', $option_name );
|
46 |
+
|
47 |
+
if ($value !== '___NONE___')
|
48 |
+
return $value;
|
49 |
+
}
|
50 |
+
|
51 |
+
throw new Exception('Option with key "' . $key . '" does not exist.');
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* wp callback
|
56 |
+
*/
|
57 |
+
public function call_wp() {
|
58 |
+
if ( ! is_admin() && ! is_feed() ) {
|
59 |
+
// add wp_head for setting js vars and css style
|
60 |
+
add_action( 'wp_head', array( $this, 'call_wp_head' ) );
|
61 |
+
|
62 |
+
// set js file
|
63 |
+
if ( $this->get_opt( 'use_js' ) )
|
64 |
+
wp_enqueue_script( 'wp-external-links', plugins_url( 'js/wp-external-links.js', WP_EXTERNAL_LINKS_FILE ), array('jquery'), WP_EXTERNAL_LINKS_VERSION, (bool) $this->get_opt( 'load_in_footer' ) );
|
65 |
+
|
66 |
+
// set ignored
|
67 |
+
$ignored = $this->get_opt( 'ignore' );
|
68 |
+
$ignored = trim( $ignored );
|
69 |
+
$ignored = explode( "\n", $ignored );
|
70 |
+
$ignored = array_map( 'trim', $ignored );
|
71 |
+
$ignored = array_map( 'strtolower', $ignored );
|
72 |
+
$this->ignored = $ignored;
|
73 |
+
|
74 |
+
// filters
|
75 |
+
if ( $this->get_opt( 'filter_page' ) ) {
|
76 |
+
// filter body
|
77 |
+
ob_start( array( $this, 'call_filter_content' ) );
|
78 |
+
|
79 |
+
// set ob flush
|
80 |
+
add_action('wp_footer', array($this, 'callback_flush_buffer'), 10000);
|
81 |
+
|
82 |
+
} else {
|
83 |
+
// set filter priority
|
84 |
+
$priority = 1000000000;
|
85 |
+
|
86 |
+
// content
|
87 |
+
if ( $this->get_opt( 'filter_posts' ) ) {
|
88 |
+
add_filter( 'the_title', array( $this, 'call_filter_content' ), $priority );
|
89 |
+
add_filter( 'the_content', array( $this, 'call_filter_content' ), $priority );
|
90 |
+
add_filter( 'get_the_excerpt', array( $this, 'call_filter_content' ), $priority );
|
91 |
+
// redundant:
|
92 |
+
//add_filter( 'the_excerpt', array( $this, 'call_filter_content' ), $priority );
|
93 |
+
}
|
94 |
+
|
95 |
+
// comments
|
96 |
+
if ( $this->get_opt( 'filter_comments' ) ) {
|
97 |
+
add_filter( 'get_comment_text', array( $this, 'call_filter_content' ), $priority );
|
98 |
+
// redundant:
|
99 |
+
//add_filter( 'comment_text', array( $this, 'call_filter_content' ), $priority );
|
100 |
+
|
101 |
+
add_filter( 'comment_excerpt', array( $this, 'call_filter_content' ), $priority );
|
102 |
+
// redundant:
|
103 |
+
//add_filter( 'get_comment_excerpt', array( $this, 'call_filter_content' ), $priority );
|
104 |
+
|
105 |
+
add_filter( 'comment_url', array( $this, 'call_filter_content' ), $priority );
|
106 |
+
add_filter( 'get_comment_author_url', array( $this, 'call_filter_content' ), $priority );
|
107 |
+
add_filter( 'get_comment_author_link', array( $this, 'call_filter_content' ), $priority );
|
108 |
+
add_filter( 'get_comment_author_url_link', array( $this, 'call_filter_content' ), $priority );
|
109 |
+
}
|
110 |
+
|
111 |
+
// widgets
|
112 |
+
if ( $this->get_opt( 'filter_widgets' ) ) {
|
113 |
+
if ( $this->admin->check_widget_content_filter() ) {
|
114 |
+
// only if Widget Logic plugin is installed and 'widget_content' option is activated
|
115 |
+
add_filter( 'widget_content', array( $this, 'call_filter_content' ), $priority );
|
116 |
+
} else {
|
117 |
+
// filter text widgets
|
118 |
+
add_filter( 'widget_title', array( $this, 'call_filter_content' ), $priority );
|
119 |
+
add_filter( 'widget_text', array( $this, 'call_filter_content' ), $priority );
|
120 |
+
}
|
121 |
+
}
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
// hook
|
126 |
+
do_action('wpel_ready', array($this, 'call_filter_content'), $this);
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* End output buffer
|
131 |
+
*/
|
132 |
+
public function callback_flush_buffer() {
|
133 |
+
ob_end_flush();
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* wp_head callback
|
138 |
+
*/
|
139 |
+
public function call_wp_head() {
|
140 |
+
$icon = $this->get_opt('icon');
|
141 |
+
|
142 |
+
if ($icon) {
|
143 |
+
$padding = ($icon < 20) ? 15 : 12;
|
144 |
+
?>
|
145 |
+
<style type="text/css" media="screen">
|
146 |
+
/* WP External Links Plugin */
|
147 |
+
.ext-icon-<?php echo $icon ?> { background:url(<?php echo plugins_url('/images/ext-icons/ext-icon-' . $icon . '.png', WP_EXTERNAL_LINKS_FILE) ?>) no-repeat 100% 50%; padding-right:<?php echo $padding ?>px; }';
|
148 |
+
</style>
|
149 |
+
<?php
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Filter content
|
155 |
+
* @param string $content
|
156 |
+
* @return string
|
157 |
+
*/
|
158 |
+
public function call_filter_content( $content ) {
|
159 |
+
if ( $this->get_opt( 'fix_js' ) ) {
|
160 |
+
// fix js problem by replacing </a> by <\/a>
|
161 |
+
$content = preg_replace_callback( '/<script([^>]*)>(.*?)<\/script[^>]*>/is', array( $this, 'call_fix_js' ), $content );
|
162 |
+
}
|
163 |
+
|
164 |
+
if ( $this->get_opt( 'phpquery' ) ) {
|
165 |
+
// Include phpQuery
|
166 |
+
if ( ! class_exists( 'phpQuery' ) ) {
|
167 |
+
require_once( 'phpQuery.php' );
|
168 |
+
}
|
169 |
+
|
170 |
+
return $this->filter_phpquery( $content );
|
171 |
+
} else {
|
172 |
+
return $this->filter( $content );
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Fix </a> in JavaScript blocks (callback for regexp)
|
178 |
+
* @param array $matches Result of a preg call in filter_content()
|
179 |
+
* @return string Clean code
|
180 |
+
*/
|
181 |
+
public function call_fix_js( $matches ) {
|
182 |
+
return str_replace( '</a>', '<\/a>', $matches[ 0 ] );
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Check if link is external
|
187 |
+
* @param string $href
|
188 |
+
* @param string $rel
|
189 |
+
* @return boolean
|
190 |
+
*/
|
191 |
+
private function is_external( $href, $rel ) {
|
192 |
+
return ( isset( $href ) AND ( ( strpos( $href, strtolower( get_bloginfo( 'wpurl' ) ) ) === FALSE )
|
193 |
+
AND ( substr( $href, 0, 7 ) == 'http://'
|
194 |
+
OR substr( $href, 0, 8 ) == 'https://'
|
195 |
+
OR substr( $href, 0, 6 ) == 'ftp://'
|
196 |
+
OR substr( $href, 0, 2 ) == '//' ) ) );
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Is an ignored link
|
201 |
+
* @param string $href
|
202 |
+
* @return boolean
|
203 |
+
*/
|
204 |
+
private function is_ignored( $href ) {
|
205 |
+
// check if this links should be ignored
|
206 |
+
for ( $x = 0, $count = count($this->ignored); $x < $count; $x++ ) {
|
207 |
+
if ( strrpos( $href, $this->ignored[ $x ] ) !== FALSE )
|
208 |
+
return TRUE;
|
209 |
+
}
|
210 |
+
|
211 |
+
return FALSE;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Filter content
|
216 |
+
* @param string $content
|
217 |
+
* @return string
|
218 |
+
*/
|
219 |
+
private function filter( $content ) {
|
220 |
+
// replace links
|
221 |
+
$content = preg_replace_callback( '/<a[^A-Za-z](.*?)>(.*?)<\/a[\s+]*>/is', array( $this, 'call_parse_link' ), $content );
|
222 |
+
|
223 |
+
// remove style when no icon classes are found
|
224 |
+
if ( strpos( $content, 'ext-icon-' ) === FALSE ) {
|
225 |
+
// remove style with id wp-external-links-css
|
226 |
+
$content = preg_replace( '/<link ([^>]*)wp-external-links-css([^>]*)\/>[\s+]*/i', '', $content );
|
227 |
+
}
|
228 |
+
|
229 |
+
return $content;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Parse an attributes string into an array. If the string starts with a tag,
|
234 |
+
* then the attributes on the first tag are parsed. This parses via a manual
|
235 |
+
* loop and is designed to be safer than using DOMDocument.
|
236 |
+
*
|
237 |
+
* @param string|* $attrs
|
238 |
+
* @return array
|
239 |
+
*
|
240 |
+
* @example parse_attrs( 'src="example.jpg" alt="example"' )
|
241 |
+
* @example parse_attrs( '<img src="example.jpg" alt="example">' )
|
242 |
+
* @example parse_attrs( '<a href="example"></a>' )
|
243 |
+
* @example parse_attrs( '<a href="example">' )
|
244 |
+
*
|
245 |
+
* @link http://dev.airve.com/demo/speed_tests/php/parse_attrs.php
|
246 |
+
*/
|
247 |
+
private function parse_attrs ($attrs) {
|
248 |
+
if ( ! is_scalar($attrs) )
|
249 |
+
return (array) $attrs;
|
250 |
+
|
251 |
+
$attrs = str_split( trim($attrs) );
|
252 |
+
|
253 |
+
if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
|
254 |
+
while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
|
255 |
+
array_shift($attrs);
|
256 |
+
|
257 |
+
$arr = array(); # output
|
258 |
+
$name = ''; # for the current attr being parsed
|
259 |
+
$value = ''; # for the current attr being parsed
|
260 |
+
$mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
|
261 |
+
$stop = false; # delimiter for the current $value being parsed
|
262 |
+
$space = ' '; # a single space
|
263 |
+
|
264 |
+
foreach ( $attrs as $j => $curr ) {
|
265 |
+
if ( $mode < 0 ) {# name
|
266 |
+
if ( '=' === $curr ) {
|
267 |
+
$mode = 1;
|
268 |
+
$stop = false;
|
269 |
+
} elseif ( '>' === $curr ) {
|
270 |
+
'' === $name or $arr[ $name ] = $value;
|
271 |
+
break;
|
272 |
+
} elseif ( ! ctype_space($curr) ) {
|
273 |
+
if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
|
274 |
+
'' === $name or $arr[ $name ] = ''; # previous name
|
275 |
+
$name = $curr; # initiate new
|
276 |
+
} else {
|
277 |
+
$name .= $curr;
|
278 |
+
}
|
279 |
+
}
|
280 |
+
} elseif ( $mode > 0 ) {# value
|
281 |
+
|
282 |
+
if ( $stop === false ) {
|
283 |
+
if ( ! ctype_space($curr) ) {
|
284 |
+
if ( '"' === $curr || "'" === $curr ) {
|
285 |
+
$value = '';
|
286 |
+
$stop = $curr;
|
287 |
+
} else {
|
288 |
+
$value = $curr;
|
289 |
+
$stop = $space;
|
290 |
+
}
|
291 |
+
}
|
292 |
+
} elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
|
293 |
+
$arr[ $name ] = $value;
|
294 |
+
$mode = 0;
|
295 |
+
$name = $value = '';
|
296 |
+
} else {
|
297 |
+
$value .= $curr;
|
298 |
+
}
|
299 |
+
} else {# neither
|
300 |
+
|
301 |
+
if ( '>' === $curr )
|
302 |
+
break;
|
303 |
+
if ( ! ctype_space( $curr ) ) {
|
304 |
+
# initiate
|
305 |
+
$name = $curr;
|
306 |
+
$mode = -1;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
}
|
310 |
+
|
311 |
+
# incl the final pair if it was quoteless
|
312 |
+
'' === $name or $arr[ $name ] = $value;
|
313 |
+
|
314 |
+
return $arr;
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Make a clean <a> code (callback for regexp)
|
319 |
+
* @param array $matches Result of a preg call in filter_content()
|
320 |
+
* @return string Clean <a> code
|
321 |
+
*/
|
322 |
+
public function call_parse_link( $matches ) {
|
323 |
+
$link = $matches[ 0 ];
|
324 |
+
$label = $matches[ 2 ];
|
325 |
+
$created_link = $link;
|
326 |
+
|
327 |
+
// parse attributes
|
328 |
+
$attrs = $matches[ 1 ];
|
329 |
+
$attrs = stripslashes( $attrs );
|
330 |
+
$attrs = $this->parse_attrs( $attrs );
|
331 |
+
|
332 |
+
$rel = ( isset( $attrs[ 'rel' ] ) ) ? strtolower( $attrs[ 'rel' ] ) : '';
|
333 |
+
|
334 |
+
// href preperation
|
335 |
+
if (isset( $attrs[ 'href' ])) {
|
336 |
+
$href = $attrs[ 'href' ];
|
337 |
+
$href = strtolower( $href );
|
338 |
+
$href = trim( $href );
|
339 |
+
} else {
|
340 |
+
$href = '';
|
341 |
+
}
|
342 |
+
|
343 |
+
// checks
|
344 |
+
$is_external = $this->is_external( $href, $rel );
|
345 |
+
$is_ignored = $this->is_ignored( $href );
|
346 |
+
$has_rel_external = (strpos( $rel, 'external' ) !== FALSE);
|
347 |
+
|
348 |
+
// is an internal link?
|
349 |
+
// rel=external will be threaded as external link
|
350 |
+
if ( ! $is_external && ! $has_rel_external) {
|
351 |
+
return apply_filters('wpel_internal_link', $created_link, $label, $attrs);
|
352 |
+
}
|
353 |
+
|
354 |
+
// is an ignored link?
|
355 |
+
// rel=external will be threaded as external link
|
356 |
+
if ( $is_ignored && ! $has_rel_external ) {
|
357 |
+
return apply_filters('wpel_external_link', $created_link, $link, $label, $attrs, TRUE);
|
358 |
+
}
|
359 |
+
|
360 |
+
// set rel="external" (when not already set)
|
361 |
+
if ( $this->get_opt( 'external' ) )
|
362 |
+
$this->add_attr_value( $attrs, 'rel', 'external' );
|
363 |
+
|
364 |
+
// set rel="nofollow" when doesn't have "follow" (or already "nofollow")
|
365 |
+
if ( $this->get_opt( 'nofollow' ) AND strpos( $rel, 'follow' ) === FALSE )
|
366 |
+
$this->add_attr_value( $attrs, 'rel', 'nofollow' );
|
367 |
+
|
368 |
+
// set title
|
369 |
+
$title_format = $this->get_opt( 'title' );
|
370 |
+
$title = ( isset( $attrs[ 'title' ] ) ) ? $attrs[ 'title' ] : '';
|
371 |
+
$attrs[ 'title' ] = str_replace( '%title%', $title, $title_format );
|
372 |
+
|
373 |
+
// set user-defined class
|
374 |
+
$class = $this->get_opt( 'class_name' );
|
375 |
+
if ( $class )
|
376 |
+
$this->add_attr_value( $attrs, 'class', $class );
|
377 |
+
|
378 |
+
// set icon class, unless no-icon class isset or another icon class ('ext-icon-...') is found or content contains image
|
379 |
+
if ( $this->get_opt( 'icon' ) > 0
|
380 |
+
AND ( ! $this->get_opt( 'no_icon_class' ) OR strpos( $attrs[ 'class' ], $this->get_opt( 'no_icon_class' ) ) === FALSE )
|
381 |
+
AND strpos( $attrs[ 'class' ], 'ext-icon-' ) === FALSE
|
382 |
+
AND !( $this->get_opt( 'image_no_icon' ) AND (bool) preg_match( '/<img([^>]*)>/is', $label )) ){
|
383 |
+
$icon_class = 'ext-icon-'. $this->get_opt( 'icon', 'style' );
|
384 |
+
$this->add_attr_value( $attrs, 'class', $icon_class );
|
385 |
+
}
|
386 |
+
|
387 |
+
// set target
|
388 |
+
$no_icon_class = $this->get_opt( 'no_icon_class' );
|
389 |
+
$target = $this->get_opt( 'target' );
|
390 |
+
|
391 |
+
// remove target
|
392 |
+
unset($attrs[ 'target' ]);
|
393 |
+
|
394 |
+
if ($this->get_opt( 'no_icon_same_window' )
|
395 |
+
AND $no_icon_class AND strpos( $attrs[ 'class' ], $no_icon_class ) !== FALSE) {
|
396 |
+
// open in same window
|
397 |
+
} elseif ($target && $target !== '_none') {
|
398 |
+
if ($this->get_opt( 'use_js' )) {
|
399 |
+
// add data-attr for javascript
|
400 |
+
$attrs['data-wpel-target'] = $target;
|
401 |
+
} else {
|
402 |
+
// set target value
|
403 |
+
$attrs[ 'target' ] = $target;
|
404 |
+
}
|
405 |
+
}
|
406 |
+
|
407 |
+
// create element code
|
408 |
+
$created_link = '<a';
|
409 |
+
|
410 |
+
foreach ( $attrs AS $key => $value ) {
|
411 |
+
$created_link .= ' '. $key .'="'. $value .'"';
|
412 |
+
}
|
413 |
+
|
414 |
+
$created_link .= '>'. $label .'</a>';
|
415 |
+
|
416 |
+
// filter
|
417 |
+
$created_link = apply_filters('wpel_external_link', $created_link, $link, $label, $attrs, FALSE);
|
418 |
+
|
419 |
+
return $created_link;
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Add value to attribute
|
424 |
+
* @param array $attrs
|
425 |
+
* @param string $attr
|
426 |
+
* @param string $value
|
427 |
+
* @param string $default Optional, default NULL which means tje attribute will be removed when (new) value is empty
|
428 |
+
* @return New value
|
429 |
+
*/
|
430 |
+
public function add_attr_value( &$attrs, $attr_name, $value, $default = NULL ) {
|
431 |
+
if ( key_exists( $attr_name, $attrs ) )
|
432 |
+
$old_value = $attrs[ $attr_name ];
|
433 |
+
|
434 |
+
if ( empty( $old_value ) )
|
435 |
+
$old_value = '';
|
436 |
+
|
437 |
+
$split = explode( ' ', strtolower( $old_value ) );
|
438 |
+
|
439 |
+
if ( in_array( $value, $split ) ) {
|
440 |
+
$value = $old_value;
|
441 |
+
} else {
|
442 |
+
$value = ( empty( $old_value ) )
|
443 |
+
? $value
|
444 |
+
: $old_value .' '. $value;
|
445 |
+
}
|
446 |
+
|
447 |
+
if ( empty( $value ) AND $default === NULL ) {
|
448 |
+
unset( $attrs[ $attr_name ] );
|
449 |
+
} else {
|
450 |
+
$attrs[ $attr_name ] = $value;
|
451 |
+
}
|
452 |
+
|
453 |
+
return $value;
|
454 |
+
}
|
455 |
+
|
456 |
+
/**
|
457 |
+
* Experimental phpQuery...
|
458 |
+
*/
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Filter content
|
462 |
+
* @param string $content
|
463 |
+
* @return string
|
464 |
+
*/
|
465 |
+
private function filter_phpquery( $content ) {
|
466 |
+
// Workaround: remove <head>-attributes before using phpQuery
|
467 |
+
$regexp_head = '/<head(>|\s(.*?)>)>/is';
|
468 |
+
$clean_head = '<head>';
|
469 |
+
|
470 |
+
// set simple <head> without attributes
|
471 |
+
preg_match( $regexp_head, $content, $matches );
|
472 |
+
|
473 |
+
if( count( $matches ) > 0 ) {
|
474 |
+
$original_head = $matches[ 0 ];
|
475 |
+
$content = str_replace( $original_head, $clean_head, $content );
|
476 |
+
}
|
477 |
+
|
478 |
+
//phpQuery::$debug = true;
|
479 |
+
|
480 |
+
// set document
|
481 |
+
$doc = phpQuery::newDocument( $content );
|
482 |
+
|
483 |
+
$excl_sel = $this->get_opt( 'filter_excl_sel' );
|
484 |
+
|
485 |
+
// set excludes
|
486 |
+
if ( ! empty( $excl_sel ) ) {
|
487 |
+
$excludes = $doc->find( $excl_sel );
|
488 |
+
$excludes->filter( 'a' )->attr( 'excluded', true );
|
489 |
+
$excludes->find( 'a' )->attr( 'excluded', true );
|
490 |
+
}
|
491 |
+
|
492 |
+
// get <a>-tags
|
493 |
+
$links = $doc->find( 'a' );
|
494 |
+
|
495 |
+
// set links
|
496 |
+
$count = count( $links );
|
497 |
+
|
498 |
+
for( $x = 0; $x < $count; $x++ ) {
|
499 |
+
$a = $links->eq( $x );
|
500 |
+
|
501 |
+
if ( ! $a->attr( 'excluded' ) )
|
502 |
+
$this->set_link_phpquery( $links->eq( $x ) );
|
503 |
+
}
|
504 |
+
|
505 |
+
// remove excluded
|
506 |
+
if ( ! empty( $excl_sel ) ) {
|
507 |
+
$excludes = $doc->find( $excl_sel );
|
508 |
+
$excludes->filter( 'a' )->removeAttr( 'excluded' );
|
509 |
+
$excludes->find( 'a' )->removeAttr( 'excluded' );
|
510 |
+
}
|
511 |
+
|
512 |
+
// remove style when no icon classes are found
|
513 |
+
if ( strpos( $doc, 'ext-icon-' ) === FALSE ) {
|
514 |
+
// remove icon css
|
515 |
+
$css = $doc->find( 'link#wp-external-links-css' )->eq(0);
|
516 |
+
$css->remove();
|
517 |
+
}
|
518 |
+
|
519 |
+
// get document content
|
520 |
+
$content = (string) $doc;
|
521 |
+
|
522 |
+
if( isset( $original_head ) ) {
|
523 |
+
// recover original <head> with attributes
|
524 |
+
$content = str_replace( $clean_head, $original_head, $content );
|
525 |
+
}
|
526 |
+
|
527 |
+
return $content;
|
528 |
+
}
|
529 |
+
|
530 |
+
/**
|
531 |
+
* Set link...
|
532 |
+
* @param Node $a
|
533 |
+
* @return Node
|
534 |
+
*/
|
535 |
+
public function set_link_phpquery( $a ) {
|
536 |
+
$href = strtolower( $a->attr( 'href' ) . '' );
|
537 |
+
$rel = strtolower( $a->attr( 'rel' ) . '' );
|
538 |
+
|
539 |
+
// check if it is an external link and not excluded
|
540 |
+
if ( ! $this->is_external( $href, $rel ) || $this->is_ignored( $href ) )
|
541 |
+
return $a;
|
542 |
+
|
543 |
+
// add "external" to rel-attribute
|
544 |
+
if ( $this->get_opt( 'external' ) ){
|
545 |
+
$this->add_attr_value_phpquery( $a, 'rel', 'external' );
|
546 |
+
}
|
547 |
+
|
548 |
+
// add "nofollow" to rel-attribute, when doesn't have "follow"
|
549 |
+
if ( $this->get_opt( 'nofollow' ) AND strpos( $rel, 'follow' ) === FALSE ){
|
550 |
+
$this->add_attr_value_phpquery( $a, 'rel', 'nofollow' );
|
551 |
+
}
|
552 |
+
|
553 |
+
// set title
|
554 |
+
$title = str_replace( '%title%', $a->attr( 'title' ), $this->get_opt( 'title' ) );
|
555 |
+
$a->attr( 'title', $title );
|
556 |
+
|
557 |
+
// add icon class, unless no-icon class isset or another icon class ('ext-icon-...') is found
|
558 |
+
if ( $this->get_opt( 'icon' ) > 0 AND ( ! $this->get_opt( 'no_icon_class' ) OR strpos( $a->attr( 'class' ), $this->get_opt( 'no_icon_class' ) ) === FALSE ) AND strpos( $a->attr( 'class' ), 'ext-icon-' ) === FALSE ){
|
559 |
+
$icon_class = 'ext-icon-'. $this->get_opt( 'icon' );
|
560 |
+
$a->addClass( $icon_class );
|
561 |
+
}
|
562 |
+
|
563 |
+
// add user-defined class
|
564 |
+
if ( $this->get_opt( 'class_name' ) ){
|
565 |
+
$a->addClass( $this->get_opt( 'class_name' ) );
|
566 |
+
}
|
567 |
+
|
568 |
+
// set target
|
569 |
+
if ( $this->get_opt( 'target' ) != '_none' AND ! $this->get_opt( 'use_js' ) AND ( ! $this->get_opt( 'no_icon_same_window' ) OR ! $this->get_opt( 'no_icon_class' ) OR strpos( $a->attr( 'class' ), $this->get_opt( 'no_icon_class' ) ) === FALSE ) )
|
570 |
+
$a->attr( 'target', $this->get_opt( 'target' ) );
|
571 |
+
|
572 |
+
return $a;
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* Add value to attribute
|
577 |
+
* @param Node $node
|
578 |
+
* @param string $attr
|
579 |
+
* @param string $value
|
580 |
+
* @return New value
|
581 |
+
*/
|
582 |
+
private function add_attr_value_phpquery( $node, $attr, $value ) {
|
583 |
+
$old_value = $node->attr( $attr );
|
584 |
+
|
585 |
+
if ( empty( $old_value ) )
|
586 |
+
$old_value = '';
|
587 |
+
|
588 |
+
$split = split( ' ', strtolower( $old_value ) );
|
589 |
+
|
590 |
+
if ( in_array( $value, $split ) ) {
|
591 |
+
$value = $old_value;
|
592 |
+
} else {
|
593 |
+
$value = ( empty( $old_value ) )
|
594 |
+
? $value
|
595 |
+
: $old_value .' '. $value;
|
596 |
+
}
|
597 |
+
|
598 |
+
$node->attr( $attr, $value );
|
599 |
+
|
600 |
+
return $value;
|
601 |
+
}
|
602 |
+
|
603 |
+
} // End WP_External_Links Class
|
604 |
+
|
605 |
+
endif;
|
606 |
+
|
607 |
+
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
includes/phpQuery.php
CHANGED
@@ -1,5702 +1,5702 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* phpQuery is a server-side, chainable, CSS3 selector driven
|
4 |
-
* Document Object Model (DOM) API based on jQuery JavaScript Library.
|
5 |
-
*
|
6 |
-
* @version 0.9.5
|
7 |
-
* @link http://code.google.com/p/phpquery/
|
8 |
-
* @link http://phpquery-library.blogspot.com/
|
9 |
-
* @link http://jquery.com/
|
10 |
-
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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, <br /> changes to <br clear="none" />
|
135 |
-
*
|
136 |
-
* @todo check XML catalogs compatibility
|
137 |
-
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
|
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 html") !== false;
|
433 |
-
// return stripos($doctype, 'xhtml') !== false;
|
434 |
-
// $doctype = isset($dom->doctype) && is_object($dom->doctype)
|
435 |
-
// ? $dom->doctype->publicId
|
436 |
-
// : self::$defaultDoctype;
|
437 |
-
}
|
438 |
-
protected function isXML($markup) {
|
439 |
-
// return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
|
440 |
-
return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
|
441 |
-
}
|
442 |
-
protected function contentTypeToArray($contentType) {
|
443 |
-
$matches = explode(';', trim(strtolower($contentType)));
|
444 |
-
if (isset($matches[1])) {
|
445 |
-
$matches[1] = explode('=', $matches[1]);
|
446 |
-
// strip 'charset='
|
447 |
-
$matches[1] = isset($matches[1][1]) && trim($matches[1][1])
|
448 |
-
? $matches[1][1]
|
449 |
-
: $matches[1][0];
|
450 |
-
} else
|
451 |
-
$matches[1] = null;
|
452 |
-
return $matches;
|
453 |
-
}
|
454 |
-
/**
|
455 |
-
*
|
456 |
-
* @param $markup
|
457 |
-
* @return array contentType, charset
|
458 |
-
*/
|
459 |
-
protected function contentTypeFromHTML($markup) {
|
460 |
-
$matches = array();
|
461 |
-
// find meta tag
|
462 |
-
preg_match('@<meta[^>]+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*<meta[^>]+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, '<head>');
|
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*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
|
512 |
-
$meta = '<meta http-equiv="Content-Type" content="text/html;charset='
|
513 |
-
.$charset.'" '
|
514 |
-
.($xhtml ? '/' : '')
|
515 |
-
.'>';
|
516 |
-
if (strpos($html, '<head') === false) {
|
517 |
-
if (strpos($hltml, '<html') === false) {
|
518 |
-
return $meta.$html;
|
519 |
-
} else {
|
520 |
-
return preg_replace(
|
521 |
-
'@<html(.*?)(?(?<!\?)>)@s',
|
522 |
-
"<html\\1><head>{$meta}</head>",
|
523 |
-
$html
|
524 |
-
);
|
525 |
-
}
|
526 |
-
} else {
|
527 |
-
return preg_replace(
|
528 |
-
'@<head(.*?)(?(?<!\?)>)@s',
|
529 |
-
'<head\\1>'.$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, '<html') === false && stripos($markup, '<!doctype') === false;
|
540 |
-
}
|
541 |
-
public static function isDocumentFragmentXML($markup) {
|
542 |
-
return stripos($markup, '<'.'?xml') === false;
|
543 |
-
}
|
544 |
-
public static function isDocumentFragmentXHTML($markup) {
|
545 |
-
return self::isDocumentFragmentHTML($markup);
|
546 |
-
}
|
547 |
-
public function importAttr($value) {
|
548 |
-
// TODO
|
549 |
-
}
|
550 |
-
/**
|
551 |
-
*
|
552 |
-
* @param $source
|
553 |
-
* @param $target
|
554 |
-
* @param $sourceCharset
|
555 |
-
* @return array Array of imported nodes.
|
556 |
-
*/
|
557 |
-
public function import($source, $sourceCharset = null) {
|
558 |
-
// TODO charset conversions
|
559 |
-
$return = array();
|
560 |
-
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
|
561 |
-
$source = array($source);
|
562 |
-
// if (is_array($source)) {
|
563 |
-
// foreach($source as $node) {
|
564 |
-
// if (is_string($node)) {
|
565 |
-
// // string markup
|
566 |
-
// $fake = $this->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('<?xml version="1.0" encoding="'.$charset.'"?>'
|
648 |
-
.'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
|
649 |
-
.'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
650 |
-
.'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
|
651 |
-
$fragment->root = $fragment->document->firstChild->nextSibling;
|
652 |
-
} else {
|
653 |
-
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
|
654 |
-
$fragment->root = $fragment->document->firstChild;
|
655 |
-
}
|
656 |
-
} else {
|
657 |
-
$markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
|
658 |
-
.$charset.'"></head>';
|
659 |
-
$noBody = strpos($markup, '<body') === false;
|
660 |
-
if ($noBody)
|
661 |
-
$markup2 .= '<body>';
|
662 |
-
$markup2 .= $markup;
|
663 |
-
if ($noBody)
|
664 |
-
$markup2 .= '</body>';
|
665 |
-
$markup2 .= '</html>';
|
666 |
-
$fragment->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, '</fake>'));
|
684 |
-
if ($fragment->isXHTML) {
|
685 |
-
$markup = substr($markup, strpos($markup, '<fake')+43);
|
686 |
-
} else {
|
687 |
-
$markup = substr($markup, strpos($markup, '<fake>')+6);
|
688 |
-
}
|
689 |
-
} else {
|
690 |
-
$markup = substr($markup, strpos($markup, '<body>')+6);
|
691 |
-
$markup = substr($markup, 0, strrpos($markup, '</body>'));
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <html> tag).
|
1171 |
-
*
|
1172 |
-
* Every document is realy a full document, so even documentFragments can
|
1173 |
-
* be queried against <html>, but getDocument(id)->htmlOuter() will return
|
1174 |
-
* only contents of <body>.
|
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 |
-
* <code>
|
1286 |
-
* $myDocumentId;
|
1287 |
-
* phpQuery::newDocument('<div/>')
|
1288 |
-
* ->getDocumentIDRef($myDocumentId)
|
1289 |
-
* ->find('div')->...
|
1290 |
-
* </code>
|
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('<pre>');
|
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("</pre>\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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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 |
-
* <p>Hello, <span>Person</span> <a href="#">and person</a></p>
|
3970 |
-
*
|
3971 |
-
* Result:
|
3972 |
-
* [ <p></p> ]
|
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 <type> $key
|
4039 |
-
* @param <type> $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 <type> $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 <tobiasz.cudnik/gmail.com>
|
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 = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
4410 |
-
"http://www.w3.org/TR/html4/loose.dtd">';
|
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('<div/>') // DOESNT accept text nodes at beginning of input string !
|
4478 |
-
* - Import into document with ID from $pq->getDocumentID():
|
4479 |
-
* pq('<div/>', $pq->getDocumentID())
|
4480 |
-
* - Import into same document as DOMNode belongs to:
|
4481 |
-
* pq('<div/>', DOMNode)
|
4482 |
-
* - Import into document from phpQuery object:
|
4483 |
-
* pq('<div/>', $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('<div/>'))
|
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('<div/>')
|
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><!-- \\3 --></php>', $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 |
-
/* <php>...</php> to <?php...? > */
|
4728 |
-
$content = preg_replace_callback(
|
4729 |
-
'@<php>\s*<!--(.*?)-->\s*</php>@s',
|
4730 |
-
// create_function('$m',
|
4731 |
-
// 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
|
4732 |
-
// ),
|
4733 |
-
array('phpQuery', '_markupToPHPCallback'),
|
4734 |
-
$content
|
4735 |
-
);
|
4736 |
-
/* <node attr='< ?php ? >'> 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]."<?php "
|
4747 |
-
.str_replace(
|
4748 |
-
array("%20", "%3E", "%09", " ", "	", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
|
4749 |
-
array(" ", ">", " ", "\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: <pre>".var_export($options, true)."</pre>\n");
|
5162 |
-
// if ($client->getCookieJar())
|
5163 |
-
// self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\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><!-- ".trim($code)." --></$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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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';
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* phpQuery is a server-side, chainable, CSS3 selector driven
|
4 |
+
* Document Object Model (DOM) API based on jQuery JavaScript Library.
|
5 |
+
*
|
6 |
+
* @version 0.9.5
|
7 |
+
* @link http://code.google.com/p/phpquery/
|
8 |
+
* @link http://phpquery-library.blogspot.com/
|
9 |
+
* @link http://jquery.com/
|
10 |
+
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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, <br /> changes to <br clear="none" />
|
135 |
+
*
|
136 |
+
* @todo check XML catalogs compatibility
|
137 |
+
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com>
|
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 html") !== false;
|
433 |
+
// return stripos($doctype, 'xhtml') !== false;
|
434 |
+
// $doctype = isset($dom->doctype) && is_object($dom->doctype)
|
435 |
+
// ? $dom->doctype->publicId
|
436 |
+
// : self::$defaultDoctype;
|
437 |
+
}
|
438 |
+
protected function isXML($markup) {
|
439 |
+
// return strpos($markup, '<?xml') !== false && stripos($markup, 'xhtml') === false;
|
440 |
+
return strpos(substr($markup, 0, 100), '<'.'?xml') !== false;
|
441 |
+
}
|
442 |
+
protected function contentTypeToArray($contentType) {
|
443 |
+
$matches = explode(';', trim(strtolower($contentType)));
|
444 |
+
if (isset($matches[1])) {
|
445 |
+
$matches[1] = explode('=', $matches[1]);
|
446 |
+
// strip 'charset='
|
447 |
+
$matches[1] = isset($matches[1][1]) && trim($matches[1][1])
|
448 |
+
? $matches[1][1]
|
449 |
+
: $matches[1][0];
|
450 |
+
} else
|
451 |
+
$matches[1] = null;
|
452 |
+
return $matches;
|
453 |
+
}
|
454 |
+
/**
|
455 |
+
*
|
456 |
+
* @param $markup
|
457 |
+
* @return array contentType, charset
|
458 |
+
*/
|
459 |
+
protected function contentTypeFromHTML($markup) {
|
460 |
+
$matches = array();
|
461 |
+
// find meta tag
|
462 |
+
preg_match('@<meta[^>]+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*<meta[^>]+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, '<head>');
|
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*<meta[^>]+http-equiv\\s*=\\s*(["|\'])Content-Type\\1([^>]+?)>@i', '', $html);
|
512 |
+
$meta = '<meta http-equiv="Content-Type" content="text/html;charset='
|
513 |
+
.$charset.'" '
|
514 |
+
.($xhtml ? '/' : '')
|
515 |
+
.'>';
|
516 |
+
if (strpos($html, '<head') === false) {
|
517 |
+
if (strpos($hltml, '<html') === false) {
|
518 |
+
return $meta.$html;
|
519 |
+
} else {
|
520 |
+
return preg_replace(
|
521 |
+
'@<html(.*?)(?(?<!\?)>)@s',
|
522 |
+
"<html\\1><head>{$meta}</head>",
|
523 |
+
$html
|
524 |
+
);
|
525 |
+
}
|
526 |
+
} else {
|
527 |
+
return preg_replace(
|
528 |
+
'@<head(.*?)(?(?<!\?)>)@s',
|
529 |
+
'<head\\1>'.$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, '<html') === false && stripos($markup, '<!doctype') === false;
|
540 |
+
}
|
541 |
+
public static function isDocumentFragmentXML($markup) {
|
542 |
+
return stripos($markup, '<'.'?xml') === false;
|
543 |
+
}
|
544 |
+
public static function isDocumentFragmentXHTML($markup) {
|
545 |
+
return self::isDocumentFragmentHTML($markup);
|
546 |
+
}
|
547 |
+
public function importAttr($value) {
|
548 |
+
// TODO
|
549 |
+
}
|
550 |
+
/**
|
551 |
+
*
|
552 |
+
* @param $source
|
553 |
+
* @param $target
|
554 |
+
* @param $sourceCharset
|
555 |
+
* @return array Array of imported nodes.
|
556 |
+
*/
|
557 |
+
public function import($source, $sourceCharset = null) {
|
558 |
+
// TODO charset conversions
|
559 |
+
$return = array();
|
560 |
+
if ($source instanceof DOMNODE && !($source instanceof DOMNODELIST))
|
561 |
+
$source = array($source);
|
562 |
+
// if (is_array($source)) {
|
563 |
+
// foreach($source as $node) {
|
564 |
+
// if (is_string($node)) {
|
565 |
+
// // string markup
|
566 |
+
// $fake = $this->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('<?xml version="1.0" encoding="'.$charset.'"?>'
|
648 |
+
.'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
|
649 |
+
.'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
650 |
+
.'<fake xmlns="http://www.w3.org/1999/xhtml">'.$markup.'</fake>');
|
651 |
+
$fragment->root = $fragment->document->firstChild->nextSibling;
|
652 |
+
} else {
|
653 |
+
$fragment->loadMarkupXML('<?xml version="1.0" encoding="'.$charset.'"?><fake>'.$markup.'</fake>');
|
654 |
+
$fragment->root = $fragment->document->firstChild;
|
655 |
+
}
|
656 |
+
} else {
|
657 |
+
$markup2 = phpQuery::$defaultDoctype.'<html><head><meta http-equiv="Content-Type" content="text/html;charset='
|
658 |
+
.$charset.'"></head>';
|
659 |
+
$noBody = strpos($markup, '<body') === false;
|
660 |
+
if ($noBody)
|
661 |
+
$markup2 .= '<body>';
|
662 |
+
$markup2 .= $markup;
|
663 |
+
if ($noBody)
|
664 |
+
$markup2 .= '</body>';
|
665 |
+
$markup2 .= '</html>';
|
666 |
+
$fragment->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, '</fake>'));
|
684 |
+
if ($fragment->isXHTML) {
|
685 |
+
$markup = substr($markup, strpos($markup, '<fake')+43);
|
686 |
+
} else {
|
687 |
+
$markup = substr($markup, strpos($markup, '<fake>')+6);
|
688 |
+
}
|
689 |
+
} else {
|
690 |
+
$markup = substr($markup, strpos($markup, '<body>')+6);
|
691 |
+
$markup = substr($markup, 0, strrpos($markup, '</body>'));
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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 <html> tag).
|
1171 |
+
*
|
1172 |
+
* Every document is realy a full document, so even documentFragments can
|
1173 |
+
* be queried against <html>, but getDocument(id)->htmlOuter() will return
|
1174 |
+
* only contents of <body>.
|
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 |
+
* <code>
|
1286 |
+
* $myDocumentId;
|
1287 |
+
* phpQuery::newDocument('<div/>')
|
1288 |
+
* ->getDocumentIDRef($myDocumentId)
|
1289 |
+
* ->find('div')->...
|
1290 |
+
* </code>
|
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('<pre>');
|
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("</pre>\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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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("<php><!-- {$content} --></php>", '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 |
+
* <p>Hello, <span>Person</span> <a href="#">and person</a></p>
|
3970 |
+
*
|
3971 |
+
* Result:
|
3972 |
+
* [ <p></p> ]
|
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 <type> $key
|
4039 |
+
* @param <type> $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 <type> $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 <tobiasz.cudnik/gmail.com>
|
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 = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
4410 |
+
"http://www.w3.org/TR/html4/loose.dtd">';
|
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('<div/>') // DOESNT accept text nodes at beginning of input string !
|
4478 |
+
* - Import into document with ID from $pq->getDocumentID():
|
4479 |
+
* pq('<div/>', $pq->getDocumentID())
|
4480 |
+
* - Import into same document as DOMNode belongs to:
|
4481 |
+
* pq('<div/>', DOMNode)
|
4482 |
+
* - Import into document from phpQuery object:
|
4483 |
+
* pq('<div/>', $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('<div/>'))
|
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('<div/>')
|
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><!-- \\3 --></php>', $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 |
+
/* <php>...</php> to <?php...? > */
|
4728 |
+
$content = preg_replace_callback(
|
4729 |
+
'@<php>\s*<!--(.*?)-->\s*</php>@s',
|
4730 |
+
// create_function('$m',
|
4731 |
+
// 'return "<'.'?php ".htmlspecialchars_decode($m[1])." ?'.'>";'
|
4732 |
+
// ),
|
4733 |
+
array('phpQuery', '_markupToPHPCallback'),
|
4734 |
+
$content
|
4735 |
+
);
|
4736 |
+
/* <node attr='< ?php ? >'> 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]."<?php "
|
4747 |
+
.str_replace(
|
4748 |
+
array("%20", "%3E", "%09", " ", "	", "%7B", "%24", "%7D", "%22", "%5B", "%5D"),
|
4749 |
+
array(" ", ">", " ", "\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: <pre>".var_export($options, true)."</pre>\n");
|
5162 |
+
// if ($client->getCookieJar())
|
5163 |
+
// self::debug("Cookies: <pre>".var_export($client->getCookieJar()->getMatchingCookies($options['url']), true)."</pre>\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><!-- ".trim($code)." --></$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 <tobiasz.cudnik/gmail.com>
|
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 <tobiasz.cudnik/gmail.com>
|
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';
|
includes/wp-plugin-dev-classes/class-wp-meta-box-page.php
CHANGED
@@ -1,664 +1,664 @@
|
|
1 |
-
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
-
if ( ! class_exists( 'WP_Meta_Box_Page_01' ) ):
|
3 |
-
/**
|
4 |
-
* WP_Meta_Box_Page_01 Class
|
5 |
-
*
|
6 |
-
* Creating a dashboard-like admin page with meta boxes
|
7 |
-
*
|
8 |
-
* Requires WordPress 3.0+ and PHP 5.2+
|
9 |
-
*
|
10 |
-
* Custom Actions:
|
11 |
-
* - load action params: (1) this object
|
12 |
-
* - meta_box_load
|
13 |
-
* - meta_box_load-[page_slug]
|
14 |
-
*
|
15 |
-
* Custom Filters:
|
16 |
-
* - page_title filter params: (1) default content, (2) this object
|
17 |
-
* - meta_box_page_title
|
18 |
-
* - meta_box_page_title-[page_slug]
|
19 |
-
*
|
20 |
-
* - page_content filter params: (1) default content, (2) this object
|
21 |
-
* - meta_box_page_content
|
22 |
-
* - meta_box_page_content-[page_slug]
|
23 |
-
*
|
24 |
-
* - screen_settings filter params: (1) default content, (2) current screen object, (3) this object
|
25 |
-
* - meta_box_screen_settings
|
26 |
-
* - meta_box_screen_settings-[page_slug]
|
27 |
-
*
|
28 |
-
* - contextual_help filter params: (1) default content, (2) current screen id, (3) current screen object, (4) this object
|
29 |
-
* - meta_box_contextual_help
|
30 |
-
* - meta_box_contextual_help-[page_slug]
|
31 |
-
*
|
32 |
-
* @version 0.1
|
33 |
-
* @author Victor Villaverde Laan
|
34 |
-
* @link http://www.freelancephp.net/
|
35 |
-
* @license Dual licensed under the MIT and GPL licenses
|
36 |
-
*/
|
37 |
-
class WP_Meta_Box_Page_01 {
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Default settings
|
41 |
-
* @var array
|
42 |
-
*/
|
43 |
-
private static $default_settings = array(
|
44 |
-
// Page title
|
45 |
-
'page_title' => NULL, // Default will be set equal to $menu_title
|
46 |
-
|
47 |
-
// Menu title
|
48 |
-
'menu_title' => NULL, // Default will be set equal to $page_title
|
49 |
-
|
50 |
-
// Page slug
|
51 |
-
'page_slug' => NULL, // Default will be set to: sanitize_title_with_dashes( $menu_title )
|
52 |
-
|
53 |
-
// Default number of columns
|
54 |
-
'default_columns' => 2,
|
55 |
-
|
56 |
-
// Column widths
|
57 |
-
'column_widths' => array(
|
58 |
-
1 => array( 99 ),
|
59 |
-
2 => array( 49, 49 ),
|
60 |
-
3 => array( 32.33, 32.33, 32.33 ),
|
61 |
-
4 => array( 24, 24, 24, 24 ),
|
62 |
-
),
|
63 |
-
|
64 |
-
// Add page method
|
65 |
-
'add_page_method' => 'add_options_page', // OR: add_menu_page, add_object_page, add_submenu_page
|
66 |
-
|
67 |
-
// Extra params for the add_page_method
|
68 |
-
|
69 |
-
// Capability
|
70 |
-
'capability' => 'manage_options', // Optional for all methods
|
71 |
-
|
72 |
-
// Parent slug
|
73 |
-
'parent_slug' => NULL, // Nescessary when using "add_submenu_page"
|
74 |
-
|
75 |
-
// Url to the icon to be used for the menu ( around 20 x 20 pixels )
|
76 |
-
'icon_url' => NULL, // Only for "add_menu_page" or "add_object_page"
|
77 |
-
|
78 |
-
// The position in the menu order this menu should appear
|
79 |
-
'position' => NULL, // Only for "add_menu_page", default on bottom of the menu
|
80 |
-
);
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Settings
|
84 |
-
* @var array
|
85 |
-
*/
|
86 |
-
private $settings = array();
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Meta boxes
|
90 |
-
* @var array
|
91 |
-
*/
|
92 |
-
private $meta_boxes = array();
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Pagehook ( will be set when page is created )
|
96 |
-
* @var string
|
97 |
-
*/
|
98 |
-
protected $pagehook = NULL;
|
99 |
-
|
100 |
-
|
101 |
-
/**
|
102 |
-
* Initialize
|
103 |
-
* @param array $settings Optional
|
104 |
-
*/
|
105 |
-
public function init( $settings = NULL, $load_callback = NULL ) {
|
106 |
-
// get settings of child class
|
107 |
-
$child_settings = $this->settings;
|
108 |
-
|
109 |
-
// set settings in 3 steps...
|
110 |
-
// (1) first set the default options
|
111 |
-
$this->set_setting( self::$default_settings );
|
112 |
-
|
113 |
-
// (2) set child settings
|
114 |
-
if ( ! empty( $child_settings ) )
|
115 |
-
$this->set_setting( $child_settings );
|
116 |
-
|
117 |
-
// (3) set param settings
|
118 |
-
if ( $settings !== NULL )
|
119 |
-
$this->set_setting( $settings );
|
120 |
-
|
121 |
-
// actions
|
122 |
-
add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
|
123 |
-
|
124 |
-
// add load action
|
125 |
-
if ( $load_callback !== NULL )
|
126 |
-
add_action( 'meta_box_load-'. $this->get_setting( 'page_slug' ), $load_callback );
|
127 |
-
}
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Set default setting value
|
131 |
-
* @param mixed $key Also possible to give an array of key/value pairs
|
132 |
-
* @param mixed $value Optional
|
133 |
-
* @static
|
134 |
-
*/
|
135 |
-
public static function set_default_setting( $key, $value = NULL ) {
|
136 |
-
if ( is_array( $key ) ) {
|
137 |
-
foreach ( $key AS $k => $v )
|
138 |
-
self::set_default_setting( $k, $v );
|
139 |
-
} else {
|
140 |
-
self::$default_settings[ $key ] = $value;
|
141 |
-
}
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Set setting value
|
146 |
-
* @param mixed $key Also possible to give an array of key/value pairs
|
147 |
-
* @param mixed $value Optional
|
148 |
-
* @return $this For chaining
|
149 |
-
*/
|
150 |
-
public function set_setting( $key, $value = NULL ) {
|
151 |
-
if ( is_array( $key ) ) {
|
152 |
-
foreach ( $key AS $k => $v )
|
153 |
-
$this->set_setting( $k, $v );
|
154 |
-
} else {
|
155 |
-
$this->settings[ $key ] = $value;
|
156 |
-
}
|
157 |
-
|
158 |
-
// auto-set related prop values
|
159 |
-
if ( $value !== NULL ) {
|
160 |
-
if ( $key == 'menu_title' AND $this->get_setting( 'page_title' ) === NULL )
|
161 |
-
$this->set_setting( 'page_title', $value );
|
162 |
-
|
163 |
-
if ( $key == 'page_title' AND $this->get_setting( 'menu_title' ) === NULL )
|
164 |
-
$this->set_setting( 'menu_title', $value );
|
165 |
-
|
166 |
-
if ( ( $key == 'menu_title' OR $key == 'page_title' ) AND $this->get_setting( 'page_slug' ) === NULL ) {
|
167 |
-
$new_val = $value;
|
168 |
-
$new_val = sanitize_title_with_dashes( $new_val );
|
169 |
-
$new_val = strtolower( $new_val );
|
170 |
-
|
171 |
-
// check for valid page_slug
|
172 |
-
if ( ! preg_match( '/^[a-z_-]+$/', $new_val ) ) {
|
173 |
-
$new_val = str_replace(
|
174 |
-
array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ),
|
175 |
-
array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' ),
|
176 |
-
$new_val
|
177 |
-
);
|
178 |
-
|
179 |
-
$new_val = ereg_replace( '[^a-z_-]', '', $new_val );
|
180 |
-
}
|
181 |
-
|
182 |
-
$this->set_setting( 'page_slug', $new_val );
|
183 |
-
}
|
184 |
-
}
|
185 |
-
|
186 |
-
return $this;
|
187 |
-
}
|
188 |
-
|
189 |
-
/**
|
190 |
-
* Get setting value
|
191 |
-
* @param string $key Optional, when NULL will return array of all options
|
192 |
-
* @param mixed $default Optional, return default when key cannot be found OR is NULL
|
193 |
-
* @return mixed
|
194 |
-
*/
|
195 |
-
public function get_setting( $key = NULL, $default = NULL ) {
|
196 |
-
if ( $key === NULL )
|
197 |
-
return $this->settings;
|
198 |
-
|
199 |
-
if ( key_exists( $key, $this->settings ) )
|
200 |
-
return ( $this->settings[ $key ] === NULL ) ? $default : $this->settings[ $key ];
|
201 |
-
|
202 |
-
return $default;
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Helper for adding a meta box
|
207 |
-
* @param string $title Title of the meta box
|
208 |
-
* @param string $callback Callback for the meta box content
|
209 |
-
* @param mixed $context Optional, add meta box to this column (normal = 1, side = 2, column3 = 3, column4 = 4)
|
210 |
-
* @param string $priority Optional, the priority within the context where the boxes should show ( 'high', 'core', 'default' or 'low' )
|
211 |
-
* @return $this For chaining
|
212 |
-
*/
|
213 |
-
public function add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default' ) {
|
214 |
-
$this->meta_boxes[] = array(
|
215 |
-
'id' => $id,
|
216 |
-
'title' => $title,
|
217 |
-
'callback' => $callback,
|
218 |
-
'context' => $context,
|
219 |
-
'priority' => $priority,
|
220 |
-
);
|
221 |
-
|
222 |
-
return $this;
|
223 |
-
}
|
224 |
-
|
225 |
-
/**
|
226 |
-
* Add callback to "meta_box_load-[page]" action, only applied for this page/object
|
227 |
-
* @param mixed $callback Callback function
|
228 |
-
* @return $this
|
229 |
-
*/
|
230 |
-
public function add_load_action( $callback ) {
|
231 |
-
add_filter( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $callback );
|
232 |
-
return $this;
|
233 |
-
}
|
234 |
-
|
235 |
-
/**
|
236 |
-
* Add callback to "meta_box_load" action, applied to all instances of this class
|
237 |
-
* @param mixed $callback Callback function
|
238 |
-
* @static
|
239 |
-
*/
|
240 |
-
public static function add_global_load_action( $callback ) {
|
241 |
-
add_filter( 'meta_box_load', $callback );
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Add callback to "meta_box_screen_settings-[page]" filter, only applied for this page/object
|
246 |
-
* @param mixed $callback Callback function
|
247 |
-
* @return $this
|
248 |
-
*/
|
249 |
-
public function add_screen_settings_filter( $callback ) {
|
250 |
-
add_filter( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $callback );
|
251 |
-
return $this;
|
252 |
-
}
|
253 |
-
|
254 |
-
/**
|
255 |
-
* Add callback to "meta_box_contextual_help-[page]" filter, only applied for this page/object
|
256 |
-
* @param mixed $callback Callback function
|
257 |
-
* @return $this
|
258 |
-
*/
|
259 |
-
public function add_contextual_help_filter( $callback ) {
|
260 |
-
add_filter( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $callback );
|
261 |
-
return $this;
|
262 |
-
}
|
263 |
-
|
264 |
-
/**
|
265 |
-
* Add callback to "meta_box_page_title-[page]" filter, only applied for this page/object
|
266 |
-
* @param mixed $callback Callback function
|
267 |
-
* @return $this
|
268 |
-
*/
|
269 |
-
public function add_title_filter( $callback ) {
|
270 |
-
add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
|
271 |
-
return $this;
|
272 |
-
}
|
273 |
-
|
274 |
-
/**
|
275 |
-
* Add callback to "meta_box_page_content" filter, applied to all instances of this class
|
276 |
-
* @param mixed $callback Callback function
|
277 |
-
* @static
|
278 |
-
*/
|
279 |
-
public static function add_global_title_filter( $callback ) {
|
280 |
-
add_filter( 'meta_box_page_title', $callback );
|
281 |
-
}
|
282 |
-
|
283 |
-
/**
|
284 |
-
* Add callback to "meta_box_page_content-[page]" filter, only applied for this page/object
|
285 |
-
* @param mixed $callback Callback function
|
286 |
-
* @return $this
|
287 |
-
*/
|
288 |
-
public function add_content_filter( $callback ) {
|
289 |
-
add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
|
290 |
-
return $this;
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* Add callback to "meta_box_page_content" filter, applied to all instances of this class
|
295 |
-
* @param mixed $callback Callback function
|
296 |
-
* @static
|
297 |
-
*/
|
298 |
-
public static function add_global_content_filter( $callback ) {
|
299 |
-
add_filter( 'meta_box_page_title', $callback );
|
300 |
-
}
|
301 |
-
|
302 |
-
/**
|
303 |
-
* Admin menu callback
|
304 |
-
*/
|
305 |
-
public function call_admin_menu() {
|
306 |
-
// add page
|
307 |
-
switch ( $this->get_setting( 'add_page_method' ) ) {
|
308 |
-
case 'add_menu_page':
|
309 |
-
$this->pagehook = add_menu_page(
|
310 |
-
$this->get_setting( 'page_title' ),
|
311 |
-
$this->get_setting( 'menu_title' ),
|
312 |
-
$this->get_setting( 'capability' ),
|
313 |
-
$this->get_setting( 'page_slug' ),
|
314 |
-
array( $this, 'call_page_content' ),
|
315 |
-
$this->get_setting( 'icon_url' ),
|
316 |
-
$this->get_setting( 'position' )
|
317 |
-
);
|
318 |
-
break;
|
319 |
-
|
320 |
-
case 'add_object_page':
|
321 |
-
$this->pagehook = add_object_page(
|
322 |
-
$this->get_setting( 'page_title' ),
|
323 |
-
$this->get_setting( 'menu_title' ),
|
324 |
-
$this->get_setting( 'capability' ),
|
325 |
-
$this->get_setting( 'page_slug' ),
|
326 |
-
array( $this, 'call_page_content' ),
|
327 |
-
$this->get_setting( 'icon_url' )
|
328 |
-
);
|
329 |
-
break;
|
330 |
-
|
331 |
-
case 'add_submenu_page':
|
332 |
-
$this->pagehook = add_submenu_page(
|
333 |
-
$this->get_setting( 'parent_slug' ),
|
334 |
-
$this->get_setting( 'page_title' ),
|
335 |
-
$this->get_setting( 'menu_title' ),
|
336 |
-
$this->get_setting( 'capability' ),
|
337 |
-
$this->get_setting( 'page_slug' ),
|
338 |
-
array( $this, 'call_page_content' )
|
339 |
-
);
|
340 |
-
break;
|
341 |
-
|
342 |
-
case 'add_options_page':
|
343 |
-
default:
|
344 |
-
$this->pagehook = add_options_page(
|
345 |
-
$this->get_setting( 'page_title' ),
|
346 |
-
$this->get_setting( 'menu_title' ),
|
347 |
-
$this->get_setting( 'capability' ),
|
348 |
-
$this->get_setting( 'page_slug' ),
|
349 |
-
array( $this, 'call_page_content' )
|
350 |
-
);
|
351 |
-
break;
|
352 |
-
|
353 |
-
}
|
354 |
-
|
355 |
-
// execute action
|
356 |
-
do_action( 'meta_box_load', $this );
|
357 |
-
|
358 |
-
// load page
|
359 |
-
add_action( 'load-' . $this->pagehook, array( $this, 'call_load_page' ) );
|
360 |
-
}
|
361 |
-
|
362 |
-
/**
|
363 |
-
* Admin head callback
|
364 |
-
*/
|
365 |
-
public function call_admin_head() {
|
366 |
-
?>
|
367 |
-
<style type="text/css">
|
368 |
-
.postbox-container { padding-right:1%; float:left ; /* for WP < 3.1 */ }
|
369 |
-
.postbox-container .meta-box-sortables { min-height:200px; }
|
370 |
-
.postbox-container .postbox { min-width:0; }
|
371 |
-
.postbox-container .postbox .inside { margin:10px 0 ; padding:0 10px; } /* for WP < 3.2 */
|
372 |
-
</style>
|
373 |
-
<?php
|
374 |
-
}
|
375 |
-
|
376 |
-
/**
|
377 |
-
* Load page callback
|
378 |
-
*/
|
379 |
-
public function call_load_page() {
|
380 |
-
// execute action
|
381 |
-
do_action( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $this );
|
382 |
-
|
383 |
-
// add script for meta boxes
|
384 |
-
wp_enqueue_script( 'postbox' );
|
385 |
-
|
386 |
-
// add to admin head
|
387 |
-
add_action( 'admin_head', array( $this, 'call_admin_head' ) );
|
388 |
-
|
389 |
-
// add screen settings filter
|
390 |
-
add_filter( 'screen_settings', array( $this, 'call_screen_settings') );
|
391 |
-
|
392 |
-
// add help text
|
393 |
-
add_filter( 'contextual_help', array( $this, 'call_contextual_help' ) );
|
394 |
-
|
395 |
-
// columns
|
396 |
-
if ( function_exists( 'add_screen_option' ) ) {
|
397 |
-
$count = count( $this->get_setting( 'column_widths' ) );
|
398 |
-
|
399 |
-
add_screen_option( 'layout_columns', array(
|
400 |
-
'max' => $count,
|
401 |
-
'default' => min( $count, $this->get_setting( 'default_columns' ) )
|
402 |
-
));
|
403 |
-
}
|
404 |
-
|
405 |
-
// add meta boxes
|
406 |
-
$nr = 0;
|
407 |
-
foreach ( $this->meta_boxes AS $box ) {
|
408 |
-
$title = $box[ 'title' ];
|
409 |
-
$id = ( isset( $box[ 'id' ] ) ) ? $box[ 'id' ] : sanitize_title_with_dashes( $title .'-'. ++$nr, 'meta-box-' . $nr );
|
410 |
-
$callback = ( is_string( $box[ 'callback' ] ) && method_exists( $this, $box[ 'callback' ] ) ) ? array( $this, $box[ 'callback' ] ) : $box[ 'callback' ];
|
411 |
-
$context = $box[ 'context' ];
|
412 |
-
$priority = $box[ 'priority' ];
|
413 |
-
|
414 |
-
// set context
|
415 |
-
if ( $context == 2 OR strtolower( $context ) == 'side' ) {
|
416 |
-
$context = 'side';
|
417 |
-
} elseif ( $context == 3 OR strtolower( $context ) == 'column3' ) {
|
418 |
-
$context = 'column3';
|
419 |
-
} elseif ( $context == 4 OR strtolower( $context ) == 'column4' ) {
|
420 |
-
$context = 'column4';
|
421 |
-
} else { // default
|
422 |
-
$context = 'normal';
|
423 |
-
}
|
424 |
-
|
425 |
-
// add meta box
|
426 |
-
add_meta_box( $id, $title, $callback, $this->pagehook, $context, $priority ); // $callback_args, doesn't seem to work
|
427 |
-
}
|
428 |
-
}
|
429 |
-
|
430 |
-
/**
|
431 |
-
* Screen settings (callback)
|
432 |
-
* @param string $content
|
433 |
-
* @return string
|
434 |
-
*/
|
435 |
-
public function call_screen_settings( $content ) {
|
436 |
-
if ( self::get_current_screen()->id == convert_to_screen( $this->pagehook )->id ) {
|
437 |
-
// apply filters for this meta box page
|
438 |
-
$content = apply_filters( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $content, $this->get_current_screen(), $this );
|
439 |
-
}
|
440 |
-
|
441 |
-
return $content;
|
442 |
-
}
|
443 |
-
|
444 |
-
/**
|
445 |
-
* Contextual help (callback)
|
446 |
-
* @param string $content
|
447 |
-
* @return string
|
448 |
-
*/
|
449 |
-
public function call_contextual_help( $content ) {
|
450 |
-
$current_screen = $this->get_current_screen();
|
451 |
-
|
452 |
-
if ( $current_screen->id == convert_to_screen( $this->pagehook )->id ) {
|
453 |
-
// apply filters for this meta box page
|
454 |
-
$content = apply_filters( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $content, $current_screen->id, $current_screen, $this );
|
455 |
-
}
|
456 |
-
|
457 |
-
return $content;
|
458 |
-
}
|
459 |
-
|
460 |
-
/**
|
461 |
-
* Display admin page content (callback)
|
462 |
-
*/
|
463 |
-
public function call_page_content() {
|
464 |
-
echo '<div class="wrap">';
|
465 |
-
|
466 |
-
// page title
|
467 |
-
$meta_boxes_page_title = apply_filters( 'meta_box_page_title', $this->get_page_title(), $this );
|
468 |
-
echo apply_filters( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $meta_boxes_page_title, $this );
|
469 |
-
|
470 |
-
// page content
|
471 |
-
$meta_boxes_content = apply_filters( 'meta_box_page_content', $this->get_page_content(), $this );
|
472 |
-
echo apply_filters( 'meta_box_page_content-' . $this->get_setting( 'page_slug' ), $meta_boxes_content, $this );
|
473 |
-
|
474 |
-
echo '</div>';
|
475 |
-
}
|
476 |
-
|
477 |
-
/**
|
478 |
-
* Get page title
|
479 |
-
* Can be changed by using adding filters, see add_title_filter()
|
480 |
-
* @return string
|
481 |
-
*/
|
482 |
-
private function get_page_title() {
|
483 |
-
return '<h2>' . get_admin_page_title() . '</h2>';
|
484 |
-
}
|
485 |
-
|
486 |
-
/**
|
487 |
-
* Get meta boxes content. Can be changed by adding filters, see add_content_filter()
|
488 |
-
* @return string
|
489 |
-
*/
|
490 |
-
private function get_page_content() {
|
491 |
-
return self::get_ob_callback( array( $this, 'show_meta_boxes' ) );
|
492 |
-
}
|
493 |
-
|
494 |
-
/**
|
495 |
-
* Display meta boxes content
|
496 |
-
*/
|
497 |
-
private function show_meta_boxes() {
|
498 |
-
$opt_column_widths = $this->get_setting( 'column_widths' );
|
499 |
-
$hide2 = $hide3 = $hide4 = '';
|
500 |
-
switch ( self::get_screen_layout_columns( $this->get_setting( 'default_columns' ) ) ) {
|
501 |
-
case 4:
|
502 |
-
$column_widths = $opt_column_widths[ 4 ];
|
503 |
-
break;
|
504 |
-
case 3:
|
505 |
-
$column_widths = $opt_column_widths[ 3 ];
|
506 |
-
$hide4 = 'display:none;';
|
507 |
-
break;
|
508 |
-
case 2:
|
509 |
-
$column_widths = $opt_column_widths[ 2 ];
|
510 |
-
$hide3 = $hide4 = 'display:none;';
|
511 |
-
break;
|
512 |
-
default:
|
513 |
-
$column_widths = $opt_column_widths[ 1 ];
|
514 |
-
$hide2 = $hide3 = $hide4 = 'display:none;';
|
515 |
-
}
|
516 |
-
|
517 |
-
$column_widths = array_pad( $column_widths, 4, 0 );
|
518 |
-
?>
|
519 |
-
<div id='<?php echo $this->pagehook ?>-widgets' class='metabox-holder'>
|
520 |
-
<div class='postbox-container' style='width:<?php echo $column_widths[0] ?>%'>
|
521 |
-
<?php do_meta_boxes( $this->pagehook, 'normal', '' ); ?>
|
522 |
-
</div>
|
523 |
-
|
524 |
-
<div class='postbox-container' style='<?php echo $hide2 ?>width:<?php echo $column_widths[1] ?>%'>
|
525 |
-
<?php do_meta_boxes( $this->pagehook, 'side', '' ); ?>
|
526 |
-
</div>
|
527 |
-
|
528 |
-
<div class='postbox-container' style='<?php echo $hide3 ?>width:<?php echo $column_widths[2] ?>%'>
|
529 |
-
<?php do_meta_boxes( $this->pagehook, 'column3', '' ); ?>
|
530 |
-
</div>
|
531 |
-
|
532 |
-
<div class='postbox-container' style='<?php echo $hide4 ?>width:<?php echo $column_widths[3] ?>%'>
|
533 |
-
<?php do_meta_boxes( $this->pagehook, 'column4', '' ); ?>
|
534 |
-
</div>
|
535 |
-
</div>
|
536 |
-
|
537 |
-
<form style="display:none" method="get" action="">
|
538 |
-
<?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
|
539 |
-
<?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
|
540 |
-
</form>
|
541 |
-
|
542 |
-
<script type="text/javascript">
|
543 |
-
//<![CDATA[
|
544 |
-
jQuery( document ).ready( function( $ ){
|
545 |
-
var columnWidths = <?php echo json_encode( $opt_column_widths ) ?>,
|
546 |
-
$boxes = $( '.postbox-container' ),
|
547 |
-
setColumnWidths = function () {
|
548 |
-
var c = $( 'input[name="screen_columns"]:checked' ).val();
|
549 |
-
|
550 |
-
// first hide all boxes
|
551 |
-
$boxes.hide();
|
552 |
-
|
553 |
-
// set width and show boxes
|
554 |
-
for ( var x = 0; x < columnWidths[ c ].length; x++ ) {
|
555 |
-
$boxes.eq( x )
|
556 |
-
.css( 'width', columnWidths[ c ][ x ]+ '%' )
|
557 |
-
.show();
|
558 |
-
}
|
559 |
-
};
|
560 |
-
|
561 |
-
// radio screen columns
|
562 |
-
$( 'input[name="screen_columns"]' )
|
563 |
-
.click(function(){
|
564 |
-
if ( $( 'input[name="screen_columns"]:checked' ).val() == $( this ).val() ) {
|
565 |
-
setTimeout(function(){
|
566 |
-
setColumnWidths();
|
567 |
-
}, 1 );
|
568 |
-
}
|
569 |
-
})
|
570 |
-
.change(function( e ){
|
571 |
-
setColumnWidths();
|
572 |
-
|
573 |
-
// prevent
|
574 |
-
e.stopImmediatePropagation();
|
575 |
-
});
|
576 |
-
|
577 |
-
// trigger change event of selected column
|
578 |
-
$( 'input[name="screen_columns"]:checked' ).change();
|
579 |
-
|
580 |
-
<?php if ( self::wp_version( '3.2', '<' ) ): ?>
|
581 |
-
// for WP < 3.2
|
582 |
-
|
583 |
-
// close postboxes that should be closed
|
584 |
-
$( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
|
585 |
-
|
586 |
-
// Loading saved screen settings
|
587 |
-
postboxes.add_postbox_toggles( '<?php echo $this->pagehook ?>' );
|
588 |
-
<?php endif; ?>
|
589 |
-
});
|
590 |
-
//]]>
|
591 |
-
</script>
|
592 |
-
<?php
|
593 |
-
}
|
594 |
-
|
595 |
-
/**
|
596 |
-
* Static helpers
|
597 |
-
*/
|
598 |
-
|
599 |
-
/**
|
600 |
-
* Get content displayed by given callback
|
601 |
-
* @param mixed $callback
|
602 |
-
* @return string
|
603 |
-
* @static
|
604 |
-
*/
|
605 |
-
public static function get_ob_callback( $callback ) {
|
606 |
-
// start output buffer
|
607 |
-
ob_start();
|
608 |
-
|
609 |
-
// call callback
|
610 |
-
call_user_func( $callback );
|
611 |
-
|
612 |
-
// get the view content
|
613 |
-
$content = ob_get_contents();
|
614 |
-
|
615 |
-
// clean output buffer
|
616 |
-
ob_end_clean();
|
617 |
-
|
618 |
-
return $content;
|
619 |
-
}
|
620 |
-
|
621 |
-
/**
|
622 |
-
* Get current version of WP or compare versions
|
623 |
-
* @global string $wp_version
|
624 |
-
* @return mixed
|
625 |
-
* @static
|
626 |
-
*/
|
627 |
-
public static function wp_version( $compare_version = NULL, $operator = NULL ) {
|
628 |
-
global $wp_version;
|
629 |
-
$cur_wp_version = preg_replace( '/-.*$/', '', $wp_version );
|
630 |
-
|
631 |
-
if ( $compare_version === NULL )
|
632 |
-
return $cur_wp_version;
|
633 |
-
|
634 |
-
// check comparison
|
635 |
-
return version_compare( $cur_wp_version, $compare_version, $operator );
|
636 |
-
}
|
637 |
-
|
638 |
-
/**
|
639 |
-
* Return global WP value of $screen_layout_columns
|
640 |
-
* @global integer $screen_layout_columns
|
641 |
-
* @return integer
|
642 |
-
* @static
|
643 |
-
*/
|
644 |
-
public static function get_screen_layout_columns( $default = NULL ) {
|
645 |
-
global $screen_layout_columns;
|
646 |
-
return ( empty( $screen_layout_columns ) ) ? $default : $screen_layout_columns;
|
647 |
-
}
|
648 |
-
|
649 |
-
/**
|
650 |
-
* Return global WP value of $current_screen
|
651 |
-
* @global string $current_screen
|
652 |
-
* @return string
|
653 |
-
* @static
|
654 |
-
*/
|
655 |
-
public static function get_current_screen( $default = NULL ) {
|
656 |
-
global $current_screen;
|
657 |
-
return $current_screen;
|
658 |
-
}
|
659 |
-
|
660 |
-
} // End WP_Meta_Box_Page_01 Class
|
661 |
-
|
662 |
-
endif;
|
663 |
-
|
664 |
-
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
1 |
+
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
+
if ( ! class_exists( 'WP_Meta_Box_Page_01' ) ):
|
3 |
+
/**
|
4 |
+
* WP_Meta_Box_Page_01 Class
|
5 |
+
*
|
6 |
+
* Creating a dashboard-like admin page with meta boxes
|
7 |
+
*
|
8 |
+
* Requires WordPress 3.0+ and PHP 5.2+
|
9 |
+
*
|
10 |
+
* Custom Actions:
|
11 |
+
* - load action params: (1) this object
|
12 |
+
* - meta_box_load
|
13 |
+
* - meta_box_load-[page_slug]
|
14 |
+
*
|
15 |
+
* Custom Filters:
|
16 |
+
* - page_title filter params: (1) default content, (2) this object
|
17 |
+
* - meta_box_page_title
|
18 |
+
* - meta_box_page_title-[page_slug]
|
19 |
+
*
|
20 |
+
* - page_content filter params: (1) default content, (2) this object
|
21 |
+
* - meta_box_page_content
|
22 |
+
* - meta_box_page_content-[page_slug]
|
23 |
+
*
|
24 |
+
* - screen_settings filter params: (1) default content, (2) current screen object, (3) this object
|
25 |
+
* - meta_box_screen_settings
|
26 |
+
* - meta_box_screen_settings-[page_slug]
|
27 |
+
*
|
28 |
+
* - contextual_help filter params: (1) default content, (2) current screen id, (3) current screen object, (4) this object
|
29 |
+
* - meta_box_contextual_help
|
30 |
+
* - meta_box_contextual_help-[page_slug]
|
31 |
+
*
|
32 |
+
* @version 0.1
|
33 |
+
* @author Victor Villaverde Laan
|
34 |
+
* @link http://www.freelancephp.net/
|
35 |
+
* @license Dual licensed under the MIT and GPL licenses
|
36 |
+
*/
|
37 |
+
class WP_Meta_Box_Page_01 {
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Default settings
|
41 |
+
* @var array
|
42 |
+
*/
|
43 |
+
private static $default_settings = array(
|
44 |
+
// Page title
|
45 |
+
'page_title' => NULL, // Default will be set equal to $menu_title
|
46 |
+
|
47 |
+
// Menu title
|
48 |
+
'menu_title' => NULL, // Default will be set equal to $page_title
|
49 |
+
|
50 |
+
// Page slug
|
51 |
+
'page_slug' => NULL, // Default will be set to: sanitize_title_with_dashes( $menu_title )
|
52 |
+
|
53 |
+
// Default number of columns
|
54 |
+
'default_columns' => 2,
|
55 |
+
|
56 |
+
// Column widths
|
57 |
+
'column_widths' => array(
|
58 |
+
1 => array( 99 ),
|
59 |
+
2 => array( 49, 49 ),
|
60 |
+
3 => array( 32.33, 32.33, 32.33 ),
|
61 |
+
4 => array( 24, 24, 24, 24 ),
|
62 |
+
),
|
63 |
+
|
64 |
+
// Add page method
|
65 |
+
'add_page_method' => 'add_options_page', // OR: add_menu_page, add_object_page, add_submenu_page
|
66 |
+
|
67 |
+
// Extra params for the add_page_method
|
68 |
+
|
69 |
+
// Capability
|
70 |
+
'capability' => 'manage_options', // Optional for all methods
|
71 |
+
|
72 |
+
// Parent slug
|
73 |
+
'parent_slug' => NULL, // Nescessary when using "add_submenu_page"
|
74 |
+
|
75 |
+
// Url to the icon to be used for the menu ( around 20 x 20 pixels )
|
76 |
+
'icon_url' => NULL, // Only for "add_menu_page" or "add_object_page"
|
77 |
+
|
78 |
+
// The position in the menu order this menu should appear
|
79 |
+
'position' => NULL, // Only for "add_menu_page", default on bottom of the menu
|
80 |
+
);
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Settings
|
84 |
+
* @var array
|
85 |
+
*/
|
86 |
+
private $settings = array();
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Meta boxes
|
90 |
+
* @var array
|
91 |
+
*/
|
92 |
+
private $meta_boxes = array();
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Pagehook ( will be set when page is created )
|
96 |
+
* @var string
|
97 |
+
*/
|
98 |
+
protected $pagehook = NULL;
|
99 |
+
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Initialize
|
103 |
+
* @param array $settings Optional
|
104 |
+
*/
|
105 |
+
public function init( $settings = NULL, $load_callback = NULL ) {
|
106 |
+
// get settings of child class
|
107 |
+
$child_settings = $this->settings;
|
108 |
+
|
109 |
+
// set settings in 3 steps...
|
110 |
+
// (1) first set the default options
|
111 |
+
$this->set_setting( self::$default_settings );
|
112 |
+
|
113 |
+
// (2) set child settings
|
114 |
+
if ( ! empty( $child_settings ) )
|
115 |
+
$this->set_setting( $child_settings );
|
116 |
+
|
117 |
+
// (3) set param settings
|
118 |
+
if ( $settings !== NULL )
|
119 |
+
$this->set_setting( $settings );
|
120 |
+
|
121 |
+
// actions
|
122 |
+
add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
|
123 |
+
|
124 |
+
// add load action
|
125 |
+
if ( $load_callback !== NULL )
|
126 |
+
add_action( 'meta_box_load-'. $this->get_setting( 'page_slug' ), $load_callback );
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Set default setting value
|
131 |
+
* @param mixed $key Also possible to give an array of key/value pairs
|
132 |
+
* @param mixed $value Optional
|
133 |
+
* @static
|
134 |
+
*/
|
135 |
+
public static function set_default_setting( $key, $value = NULL ) {
|
136 |
+
if ( is_array( $key ) ) {
|
137 |
+
foreach ( $key AS $k => $v )
|
138 |
+
self::set_default_setting( $k, $v );
|
139 |
+
} else {
|
140 |
+
self::$default_settings[ $key ] = $value;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Set setting value
|
146 |
+
* @param mixed $key Also possible to give an array of key/value pairs
|
147 |
+
* @param mixed $value Optional
|
148 |
+
* @return $this For chaining
|
149 |
+
*/
|
150 |
+
public function set_setting( $key, $value = NULL ) {
|
151 |
+
if ( is_array( $key ) ) {
|
152 |
+
foreach ( $key AS $k => $v )
|
153 |
+
$this->set_setting( $k, $v );
|
154 |
+
} else {
|
155 |
+
$this->settings[ $key ] = $value;
|
156 |
+
}
|
157 |
+
|
158 |
+
// auto-set related prop values
|
159 |
+
if ( $value !== NULL ) {
|
160 |
+
if ( $key == 'menu_title' AND $this->get_setting( 'page_title' ) === NULL )
|
161 |
+
$this->set_setting( 'page_title', $value );
|
162 |
+
|
163 |
+
if ( $key == 'page_title' AND $this->get_setting( 'menu_title' ) === NULL )
|
164 |
+
$this->set_setting( 'menu_title', $value );
|
165 |
+
|
166 |
+
if ( ( $key == 'menu_title' OR $key == 'page_title' ) AND $this->get_setting( 'page_slug' ) === NULL ) {
|
167 |
+
$new_val = $value;
|
168 |
+
$new_val = sanitize_title_with_dashes( $new_val );
|
169 |
+
$new_val = strtolower( $new_val );
|
170 |
+
|
171 |
+
// check for valid page_slug
|
172 |
+
if ( ! preg_match( '/^[a-z_-]+$/', $new_val ) ) {
|
173 |
+
$new_val = str_replace(
|
174 |
+
array( 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ),
|
175 |
+
array( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' ),
|
176 |
+
$new_val
|
177 |
+
);
|
178 |
+
|
179 |
+
$new_val = ereg_replace( '[^a-z_-]', '', $new_val );
|
180 |
+
}
|
181 |
+
|
182 |
+
$this->set_setting( 'page_slug', $new_val );
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
return $this;
|
187 |
+
}
|
188 |
+
|
189 |
+
/**
|
190 |
+
* Get setting value
|
191 |
+
* @param string $key Optional, when NULL will return array of all options
|
192 |
+
* @param mixed $default Optional, return default when key cannot be found OR is NULL
|
193 |
+
* @return mixed
|
194 |
+
*/
|
195 |
+
public function get_setting( $key = NULL, $default = NULL ) {
|
196 |
+
if ( $key === NULL )
|
197 |
+
return $this->settings;
|
198 |
+
|
199 |
+
if ( key_exists( $key, $this->settings ) )
|
200 |
+
return ( $this->settings[ $key ] === NULL ) ? $default : $this->settings[ $key ];
|
201 |
+
|
202 |
+
return $default;
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Helper for adding a meta box
|
207 |
+
* @param string $title Title of the meta box
|
208 |
+
* @param string $callback Callback for the meta box content
|
209 |
+
* @param mixed $context Optional, add meta box to this column (normal = 1, side = 2, column3 = 3, column4 = 4)
|
210 |
+
* @param string $priority Optional, the priority within the context where the boxes should show ( 'high', 'core', 'default' or 'low' )
|
211 |
+
* @return $this For chaining
|
212 |
+
*/
|
213 |
+
public function add_meta_box( $title, $callback, $context = 'normal', $id = NULL, $priority = 'default' ) {
|
214 |
+
$this->meta_boxes[] = array(
|
215 |
+
'id' => $id,
|
216 |
+
'title' => $title,
|
217 |
+
'callback' => $callback,
|
218 |
+
'context' => $context,
|
219 |
+
'priority' => $priority,
|
220 |
+
);
|
221 |
+
|
222 |
+
return $this;
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Add callback to "meta_box_load-[page]" action, only applied for this page/object
|
227 |
+
* @param mixed $callback Callback function
|
228 |
+
* @return $this
|
229 |
+
*/
|
230 |
+
public function add_load_action( $callback ) {
|
231 |
+
add_filter( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $callback );
|
232 |
+
return $this;
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Add callback to "meta_box_load" action, applied to all instances of this class
|
237 |
+
* @param mixed $callback Callback function
|
238 |
+
* @static
|
239 |
+
*/
|
240 |
+
public static function add_global_load_action( $callback ) {
|
241 |
+
add_filter( 'meta_box_load', $callback );
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* Add callback to "meta_box_screen_settings-[page]" filter, only applied for this page/object
|
246 |
+
* @param mixed $callback Callback function
|
247 |
+
* @return $this
|
248 |
+
*/
|
249 |
+
public function add_screen_settings_filter( $callback ) {
|
250 |
+
add_filter( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $callback );
|
251 |
+
return $this;
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Add callback to "meta_box_contextual_help-[page]" filter, only applied for this page/object
|
256 |
+
* @param mixed $callback Callback function
|
257 |
+
* @return $this
|
258 |
+
*/
|
259 |
+
public function add_contextual_help_filter( $callback ) {
|
260 |
+
add_filter( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $callback );
|
261 |
+
return $this;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Add callback to "meta_box_page_title-[page]" filter, only applied for this page/object
|
266 |
+
* @param mixed $callback Callback function
|
267 |
+
* @return $this
|
268 |
+
*/
|
269 |
+
public function add_title_filter( $callback ) {
|
270 |
+
add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
|
271 |
+
return $this;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Add callback to "meta_box_page_content" filter, applied to all instances of this class
|
276 |
+
* @param mixed $callback Callback function
|
277 |
+
* @static
|
278 |
+
*/
|
279 |
+
public static function add_global_title_filter( $callback ) {
|
280 |
+
add_filter( 'meta_box_page_title', $callback );
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* Add callback to "meta_box_page_content-[page]" filter, only applied for this page/object
|
285 |
+
* @param mixed $callback Callback function
|
286 |
+
* @return $this
|
287 |
+
*/
|
288 |
+
public function add_content_filter( $callback ) {
|
289 |
+
add_filter( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $callback );
|
290 |
+
return $this;
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* Add callback to "meta_box_page_content" filter, applied to all instances of this class
|
295 |
+
* @param mixed $callback Callback function
|
296 |
+
* @static
|
297 |
+
*/
|
298 |
+
public static function add_global_content_filter( $callback ) {
|
299 |
+
add_filter( 'meta_box_page_title', $callback );
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Admin menu callback
|
304 |
+
*/
|
305 |
+
public function call_admin_menu() {
|
306 |
+
// add page
|
307 |
+
switch ( $this->get_setting( 'add_page_method' ) ) {
|
308 |
+
case 'add_menu_page':
|
309 |
+
$this->pagehook = add_menu_page(
|
310 |
+
$this->get_setting( 'page_title' ),
|
311 |
+
$this->get_setting( 'menu_title' ),
|
312 |
+
$this->get_setting( 'capability' ),
|
313 |
+
$this->get_setting( 'page_slug' ),
|
314 |
+
array( $this, 'call_page_content' ),
|
315 |
+
$this->get_setting( 'icon_url' ),
|
316 |
+
$this->get_setting( 'position' )
|
317 |
+
);
|
318 |
+
break;
|
319 |
+
|
320 |
+
case 'add_object_page':
|
321 |
+
$this->pagehook = add_object_page(
|
322 |
+
$this->get_setting( 'page_title' ),
|
323 |
+
$this->get_setting( 'menu_title' ),
|
324 |
+
$this->get_setting( 'capability' ),
|
325 |
+
$this->get_setting( 'page_slug' ),
|
326 |
+
array( $this, 'call_page_content' ),
|
327 |
+
$this->get_setting( 'icon_url' )
|
328 |
+
);
|
329 |
+
break;
|
330 |
+
|
331 |
+
case 'add_submenu_page':
|
332 |
+
$this->pagehook = add_submenu_page(
|
333 |
+
$this->get_setting( 'parent_slug' ),
|
334 |
+
$this->get_setting( 'page_title' ),
|
335 |
+
$this->get_setting( 'menu_title' ),
|
336 |
+
$this->get_setting( 'capability' ),
|
337 |
+
$this->get_setting( 'page_slug' ),
|
338 |
+
array( $this, 'call_page_content' )
|
339 |
+
);
|
340 |
+
break;
|
341 |
+
|
342 |
+
case 'add_options_page':
|
343 |
+
default:
|
344 |
+
$this->pagehook = add_options_page(
|
345 |
+
$this->get_setting( 'page_title' ),
|
346 |
+
$this->get_setting( 'menu_title' ),
|
347 |
+
$this->get_setting( 'capability' ),
|
348 |
+
$this->get_setting( 'page_slug' ),
|
349 |
+
array( $this, 'call_page_content' )
|
350 |
+
);
|
351 |
+
break;
|
352 |
+
|
353 |
+
}
|
354 |
+
|
355 |
+
// execute action
|
356 |
+
do_action( 'meta_box_load', $this );
|
357 |
+
|
358 |
+
// load page
|
359 |
+
add_action( 'load-' . $this->pagehook, array( $this, 'call_load_page' ) );
|
360 |
+
}
|
361 |
+
|
362 |
+
/**
|
363 |
+
* Admin head callback
|
364 |
+
*/
|
365 |
+
public function call_admin_head() {
|
366 |
+
?>
|
367 |
+
<style type="text/css">
|
368 |
+
.postbox-container { padding-right:1%; float:left ; /* for WP < 3.1 */ }
|
369 |
+
.postbox-container .meta-box-sortables { min-height:200px; }
|
370 |
+
.postbox-container .postbox { min-width:0; }
|
371 |
+
.postbox-container .postbox .inside { margin:10px 0 ; padding:0 10px; } /* for WP < 3.2 */
|
372 |
+
</style>
|
373 |
+
<?php
|
374 |
+
}
|
375 |
+
|
376 |
+
/**
|
377 |
+
* Load page callback
|
378 |
+
*/
|
379 |
+
public function call_load_page() {
|
380 |
+
// execute action
|
381 |
+
do_action( 'meta_box_load-' . $this->get_setting( 'page_slug' ), $this );
|
382 |
+
|
383 |
+
// add script for meta boxes
|
384 |
+
wp_enqueue_script( 'postbox' );
|
385 |
+
|
386 |
+
// add to admin head
|
387 |
+
add_action( 'admin_head', array( $this, 'call_admin_head' ) );
|
388 |
+
|
389 |
+
// add screen settings filter
|
390 |
+
add_filter( 'screen_settings', array( $this, 'call_screen_settings') );
|
391 |
+
|
392 |
+
// add help text
|
393 |
+
add_filter( 'contextual_help', array( $this, 'call_contextual_help' ) );
|
394 |
+
|
395 |
+
// columns
|
396 |
+
if ( function_exists( 'add_screen_option' ) ) {
|
397 |
+
$count = count( $this->get_setting( 'column_widths' ) );
|
398 |
+
|
399 |
+
add_screen_option( 'layout_columns', array(
|
400 |
+
'max' => $count,
|
401 |
+
'default' => min( $count, $this->get_setting( 'default_columns' ) )
|
402 |
+
));
|
403 |
+
}
|
404 |
+
|
405 |
+
// add meta boxes
|
406 |
+
$nr = 0;
|
407 |
+
foreach ( $this->meta_boxes AS $box ) {
|
408 |
+
$title = $box[ 'title' ];
|
409 |
+
$id = ( isset( $box[ 'id' ] ) ) ? $box[ 'id' ] : sanitize_title_with_dashes( $title .'-'. ++$nr, 'meta-box-' . $nr );
|
410 |
+
$callback = ( is_string( $box[ 'callback' ] ) && method_exists( $this, $box[ 'callback' ] ) ) ? array( $this, $box[ 'callback' ] ) : $box[ 'callback' ];
|
411 |
+
$context = $box[ 'context' ];
|
412 |
+
$priority = $box[ 'priority' ];
|
413 |
+
|
414 |
+
// set context
|
415 |
+
if ( $context == 2 OR strtolower( $context ) == 'side' ) {
|
416 |
+
$context = 'side';
|
417 |
+
} elseif ( $context == 3 OR strtolower( $context ) == 'column3' ) {
|
418 |
+
$context = 'column3';
|
419 |
+
} elseif ( $context == 4 OR strtolower( $context ) == 'column4' ) {
|
420 |
+
$context = 'column4';
|
421 |
+
} else { // default
|
422 |
+
$context = 'normal';
|
423 |
+
}
|
424 |
+
|
425 |
+
// add meta box
|
426 |
+
add_meta_box( $id, $title, $callback, $this->pagehook, $context, $priority ); // $callback_args, doesn't seem to work
|
427 |
+
}
|
428 |
+
}
|
429 |
+
|
430 |
+
/**
|
431 |
+
* Screen settings (callback)
|
432 |
+
* @param string $content
|
433 |
+
* @return string
|
434 |
+
*/
|
435 |
+
public function call_screen_settings( $content ) {
|
436 |
+
if ( self::get_current_screen()->id == convert_to_screen( $this->pagehook )->id ) {
|
437 |
+
// apply filters for this meta box page
|
438 |
+
$content = apply_filters( 'meta_box_screen_settings-' . $this->get_setting( 'page_slug' ), $content, $this->get_current_screen(), $this );
|
439 |
+
}
|
440 |
+
|
441 |
+
return $content;
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Contextual help (callback)
|
446 |
+
* @param string $content
|
447 |
+
* @return string
|
448 |
+
*/
|
449 |
+
public function call_contextual_help( $content ) {
|
450 |
+
$current_screen = $this->get_current_screen();
|
451 |
+
|
452 |
+
if ( $current_screen->id == convert_to_screen( $this->pagehook )->id ) {
|
453 |
+
// apply filters for this meta box page
|
454 |
+
$content = apply_filters( 'meta_box_contextual_help-' . $this->get_setting( 'page_slug' ), $content, $current_screen->id, $current_screen, $this );
|
455 |
+
}
|
456 |
+
|
457 |
+
return $content;
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Display admin page content (callback)
|
462 |
+
*/
|
463 |
+
public function call_page_content() {
|
464 |
+
echo '<div class="wrap">';
|
465 |
+
|
466 |
+
// page title
|
467 |
+
$meta_boxes_page_title = apply_filters( 'meta_box_page_title', $this->get_page_title(), $this );
|
468 |
+
echo apply_filters( 'meta_box_page_title-' . $this->get_setting( 'page_slug' ), $meta_boxes_page_title, $this );
|
469 |
+
|
470 |
+
// page content
|
471 |
+
$meta_boxes_content = apply_filters( 'meta_box_page_content', $this->get_page_content(), $this );
|
472 |
+
echo apply_filters( 'meta_box_page_content-' . $this->get_setting( 'page_slug' ), $meta_boxes_content, $this );
|
473 |
+
|
474 |
+
echo '</div>';
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Get page title
|
479 |
+
* Can be changed by using adding filters, see add_title_filter()
|
480 |
+
* @return string
|
481 |
+
*/
|
482 |
+
private function get_page_title() {
|
483 |
+
return '<h2>' . get_admin_page_title() . '</h2>';
|
484 |
+
}
|
485 |
+
|
486 |
+
/**
|
487 |
+
* Get meta boxes content. Can be changed by adding filters, see add_content_filter()
|
488 |
+
* @return string
|
489 |
+
*/
|
490 |
+
private function get_page_content() {
|
491 |
+
return self::get_ob_callback( array( $this, 'show_meta_boxes' ) );
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Display meta boxes content
|
496 |
+
*/
|
497 |
+
private function show_meta_boxes() {
|
498 |
+
$opt_column_widths = $this->get_setting( 'column_widths' );
|
499 |
+
$hide2 = $hide3 = $hide4 = '';
|
500 |
+
switch ( self::get_screen_layout_columns( $this->get_setting( 'default_columns' ) ) ) {
|
501 |
+
case 4:
|
502 |
+
$column_widths = $opt_column_widths[ 4 ];
|
503 |
+
break;
|
504 |
+
case 3:
|
505 |
+
$column_widths = $opt_column_widths[ 3 ];
|
506 |
+
$hide4 = 'display:none;';
|
507 |
+
break;
|
508 |
+
case 2:
|
509 |
+
$column_widths = $opt_column_widths[ 2 ];
|
510 |
+
$hide3 = $hide4 = 'display:none;';
|
511 |
+
break;
|
512 |
+
default:
|
513 |
+
$column_widths = $opt_column_widths[ 1 ];
|
514 |
+
$hide2 = $hide3 = $hide4 = 'display:none;';
|
515 |
+
}
|
516 |
+
|
517 |
+
$column_widths = array_pad( $column_widths, 4, 0 );
|
518 |
+
?>
|
519 |
+
<div id='<?php echo $this->pagehook ?>-widgets' class='metabox-holder'>
|
520 |
+
<div class='postbox-container' style='width:<?php echo $column_widths[0] ?>%'>
|
521 |
+
<?php do_meta_boxes( $this->pagehook, 'normal', '' ); ?>
|
522 |
+
</div>
|
523 |
+
|
524 |
+
<div class='postbox-container' style='<?php echo $hide2 ?>width:<?php echo $column_widths[1] ?>%'>
|
525 |
+
<?php do_meta_boxes( $this->pagehook, 'side', '' ); ?>
|
526 |
+
</div>
|
527 |
+
|
528 |
+
<div class='postbox-container' style='<?php echo $hide3 ?>width:<?php echo $column_widths[2] ?>%'>
|
529 |
+
<?php do_meta_boxes( $this->pagehook, 'column3', '' ); ?>
|
530 |
+
</div>
|
531 |
+
|
532 |
+
<div class='postbox-container' style='<?php echo $hide4 ?>width:<?php echo $column_widths[3] ?>%'>
|
533 |
+
<?php do_meta_boxes( $this->pagehook, 'column4', '' ); ?>
|
534 |
+
</div>
|
535 |
+
</div>
|
536 |
+
|
537 |
+
<form style="display:none" method="get" action="">
|
538 |
+
<?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
|
539 |
+
<?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
|
540 |
+
</form>
|
541 |
+
|
542 |
+
<script type="text/javascript">
|
543 |
+
//<![CDATA[
|
544 |
+
jQuery( document ).ready( function( $ ){
|
545 |
+
var columnWidths = <?php echo json_encode( $opt_column_widths ) ?>,
|
546 |
+
$boxes = $( '.postbox-container' ),
|
547 |
+
setColumnWidths = function () {
|
548 |
+
var c = $( 'input[name="screen_columns"]:checked' ).val();
|
549 |
+
|
550 |
+
// first hide all boxes
|
551 |
+
$boxes.hide();
|
552 |
+
|
553 |
+
// set width and show boxes
|
554 |
+
for ( var x = 0; x < columnWidths[ c ].length; x++ ) {
|
555 |
+
$boxes.eq( x )
|
556 |
+
.css( 'width', columnWidths[ c ][ x ]+ '%' )
|
557 |
+
.show();
|
558 |
+
}
|
559 |
+
};
|
560 |
+
|
561 |
+
// radio screen columns
|
562 |
+
$( 'input[name="screen_columns"]' )
|
563 |
+
.click(function(){
|
564 |
+
if ( $( 'input[name="screen_columns"]:checked' ).val() == $( this ).val() ) {
|
565 |
+
setTimeout(function(){
|
566 |
+
setColumnWidths();
|
567 |
+
}, 1 );
|
568 |
+
}
|
569 |
+
})
|
570 |
+
.change(function( e ){
|
571 |
+
setColumnWidths();
|
572 |
+
|
573 |
+
// prevent
|
574 |
+
e.stopImmediatePropagation();
|
575 |
+
});
|
576 |
+
|
577 |
+
// trigger change event of selected column
|
578 |
+
$( 'input[name="screen_columns"]:checked' ).change();
|
579 |
+
|
580 |
+
<?php if ( self::wp_version( '3.2', '<' ) ): ?>
|
581 |
+
// for WP < 3.2
|
582 |
+
|
583 |
+
// close postboxes that should be closed
|
584 |
+
$( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
|
585 |
+
|
586 |
+
// Loading saved screen settings
|
587 |
+
postboxes.add_postbox_toggles( '<?php echo $this->pagehook ?>' );
|
588 |
+
<?php endif; ?>
|
589 |
+
});
|
590 |
+
//]]>
|
591 |
+
</script>
|
592 |
+
<?php
|
593 |
+
}
|
594 |
+
|
595 |
+
/**
|
596 |
+
* Static helpers
|
597 |
+
*/
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Get content displayed by given callback
|
601 |
+
* @param mixed $callback
|
602 |
+
* @return string
|
603 |
+
* @static
|
604 |
+
*/
|
605 |
+
public static function get_ob_callback( $callback ) {
|
606 |
+
// start output buffer
|
607 |
+
ob_start();
|
608 |
+
|
609 |
+
// call callback
|
610 |
+
call_user_func( $callback );
|
611 |
+
|
612 |
+
// get the view content
|
613 |
+
$content = ob_get_contents();
|
614 |
+
|
615 |
+
// clean output buffer
|
616 |
+
ob_end_clean();
|
617 |
+
|
618 |
+
return $content;
|
619 |
+
}
|
620 |
+
|
621 |
+
/**
|
622 |
+
* Get current version of WP or compare versions
|
623 |
+
* @global string $wp_version
|
624 |
+
* @return mixed
|
625 |
+
* @static
|
626 |
+
*/
|
627 |
+
public static function wp_version( $compare_version = NULL, $operator = NULL ) {
|
628 |
+
global $wp_version;
|
629 |
+
$cur_wp_version = preg_replace( '/-.*$/', '', $wp_version );
|
630 |
+
|
631 |
+
if ( $compare_version === NULL )
|
632 |
+
return $cur_wp_version;
|
633 |
+
|
634 |
+
// check comparison
|
635 |
+
return version_compare( $cur_wp_version, $compare_version, $operator );
|
636 |
+
}
|
637 |
+
|
638 |
+
/**
|
639 |
+
* Return global WP value of $screen_layout_columns
|
640 |
+
* @global integer $screen_layout_columns
|
641 |
+
* @return integer
|
642 |
+
* @static
|
643 |
+
*/
|
644 |
+
public static function get_screen_layout_columns( $default = NULL ) {
|
645 |
+
global $screen_layout_columns;
|
646 |
+
return ( empty( $screen_layout_columns ) ) ? $default : $screen_layout_columns;
|
647 |
+
}
|
648 |
+
|
649 |
+
/**
|
650 |
+
* Return global WP value of $current_screen
|
651 |
+
* @global string $current_screen
|
652 |
+
* @return string
|
653 |
+
* @static
|
654 |
+
*/
|
655 |
+
public static function get_current_screen( $default = NULL ) {
|
656 |
+
global $current_screen;
|
657 |
+
return $current_screen;
|
658 |
+
}
|
659 |
+
|
660 |
+
} // End WP_Meta_Box_Page_01 Class
|
661 |
+
|
662 |
+
endif;
|
663 |
+
|
664 |
+
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
includes/wp-plugin-dev-classes/class-wp-option-forms.php
CHANGED
@@ -1,360 +1,360 @@
|
|
1 |
-
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
-
if ( ! class_exists( 'WP_Option_Forms_01' ) ):
|
3 |
-
/**
|
4 |
-
* WP_Option_Forms_01 Class
|
5 |
-
*
|
6 |
-
* Simple class for creating option forms of a the same option_group.
|
7 |
-
* Also with Ajax save support.
|
8 |
-
*
|
9 |
-
* Requires WordPress 3.0+ and PHP 5.2+
|
10 |
-
*
|
11 |
-
* @version 0.1
|
12 |
-
* @author Victor Villaverde Laan
|
13 |
-
* @link http://www.freelancephp.net/
|
14 |
-
* @license Dual licensed under the MIT and GPL licenses
|
15 |
-
*/
|
16 |
-
class WP_Option_Forms_01 {
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Name used as prefix for saving option names
|
20 |
-
* @var string
|
21 |
-
*/
|
22 |
-
protected $name = NULL;
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Option names and values
|
26 |
-
* @var string
|
27 |
-
*/
|
28 |
-
protected $options = array();
|
29 |
-
|
30 |
-
/**
|
31 |
-
* Current option name
|
32 |
-
* @var string
|
33 |
-
*/
|
34 |
-
protected $current_option = NULL;
|
35 |
-
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Constructor
|
39 |
-
* @param array $name
|
40 |
-
* @param array $options Optional
|
41 |
-
*/
|
42 |
-
public function __construct( $name, $options = array() ) {
|
43 |
-
$this->name = sanitize_title_with_dashes( $name );
|
44 |
-
|
45 |
-
// set option names
|
46 |
-
foreach ( $options AS $option_name => $values ) {
|
47 |
-
$this->add_option( $option_name, $values );
|
48 |
-
}
|
49 |
-
|
50 |
-
// actions
|
51 |
-
add_action( 'wp_ajax_wpof_update_options', array( $this, 'call_wp_ajax' ) );
|
52 |
-
add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Admin menu callback
|
57 |
-
*/
|
58 |
-
public function call_admin_menu() {
|
59 |
-
// Register settings
|
60 |
-
foreach ( $this->options AS $option_name => $values ) {
|
61 |
-
register_setting( $option_name, $option_name );
|
62 |
-
}
|
63 |
-
|
64 |
-
// script
|
65 |
-
// wp_enqueue_script( 'option-forms', plugins_url( 'js/wp-option-forms.js', WP_EXTERNAL_LINKS_FILE ), array( 'jquery' ), '1.0' );
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Ajax call for saving option values
|
70 |
-
*/
|
71 |
-
public function call_wp_ajax() {
|
72 |
-
check_ajax_referer( 'wpof_update_options', 'wpof-nonce' );
|
73 |
-
|
74 |
-
$option_name = $_POST[ 'ajax_option_name' ];
|
75 |
-
$value = NULL;
|
76 |
-
|
77 |
-
if ( isset( $_POST[ $option_name ] ) )
|
78 |
-
$value = $_POST[ $option_name ];
|
79 |
-
|
80 |
-
if ( ! is_array( $value ) )
|
81 |
-
$value = trim( $value );
|
82 |
-
|
83 |
-
$value = stripslashes_deep( $value );
|
84 |
-
|
85 |
-
update_option( $option_name, $value );
|
86 |
-
|
87 |
-
die( '1' );
|
88 |
-
}
|
89 |
-
|
90 |
-
/**
|
91 |
-
* Add option (or reset option when already exists)
|
92 |
-
* @param string $option_name
|
93 |
-
* @param array $default_values Optional
|
94 |
-
* @return this
|
95 |
-
*/
|
96 |
-
public function add_option( $option_name, $default_values = array() ) {
|
97 |
-
// set values
|
98 |
-
$saved_values = get_option( $this->name .'-'. $option_name );
|
99 |
-
|
100 |
-
if ( empty( $saved_values ) ) {
|
101 |
-
foreach ( $default_values AS $key => $value )
|
102 |
-
$values[ $key ] = $value;
|
103 |
-
} else {
|
104 |
-
foreach ( $default_values AS $key => $value )
|
105 |
-
$values[ $key ] = '';
|
106 |
-
|
107 |
-
foreach ( $saved_values AS $key => $value )
|
108 |
-
$values[ $key ] = $value;
|
109 |
-
}
|
110 |
-
|
111 |
-
// option and values
|
112 |
-
$this->options[ $this->name .'-'. $option_name ] = $values;
|
113 |
-
return $this;
|
114 |
-
}
|
115 |
-
|
116 |
-
/**
|
117 |
-
* Set current option to use
|
118 |
-
* @param string $option_name
|
119 |
-
* @return this
|
120 |
-
*/
|
121 |
-
public function set_current_option( $option_name ) {
|
122 |
-
$this->current_option = $this->name .'-'. $option_name;
|
123 |
-
return $this;
|
124 |
-
}
|
125 |
-
|
126 |
-
/**
|
127 |
-
* Get opening form with all nescessary WP fields
|
128 |
-
* @param boolean $ajaxSave Optional
|
129 |
-
* @param array $attrs Optional
|
130 |
-
* @return string
|
131 |
-
*/
|
132 |
-
public function open_form( $ajaxSave = TRUE, $attrs = array() ) {
|
133 |
-
// set class for ajax or non-ajax form
|
134 |
-
$attrs[ 'class' ] = ( ( $ajaxSave ) ? 'ajax-form' : 'no-ajax-form' )
|
135 |
-
. ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
|
136 |
-
|
137 |
-
// show start form
|
138 |
-
$html = '';
|
139 |
-
$html .= '<form method="post" action="options.php" '. $this->attrs( $attrs ) .'>';
|
140 |
-
|
141 |
-
if ( $ajaxSave ) {
|
142 |
-
$html .= wp_nonce_field( 'wpof_update_options', 'wpof-nonce', FALSE, FALSE );
|
143 |
-
$html .= '<input type="hidden" name="action" value="wpof_update_options" />';
|
144 |
-
$html .= '<input type="hidden" name="ajax_option_name" value="'. $this->current_option .'" />';
|
145 |
-
|
146 |
-
// instead of using settings_fields();
|
147 |
-
$html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
|
148 |
-
$html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
|
149 |
-
} else {
|
150 |
-
// instead of using settings_fields();
|
151 |
-
$html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
|
152 |
-
$html .= '<input type="hidden" name="action" value="update" />';
|
153 |
-
$html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
|
154 |
-
}
|
155 |
-
|
156 |
-
return $html;
|
157 |
-
}
|
158 |
-
|
159 |
-
/**
|
160 |
-
* Get script for saving screen option
|
161 |
-
* @param string $option_name
|
162 |
-
* @param string $key
|
163 |
-
* @return string
|
164 |
-
*/
|
165 |
-
public function open_screen_option( $option_name, $key ) {
|
166 |
-
$this->set_current_option( $option_name );
|
167 |
-
|
168 |
-
$html = '';
|
169 |
-
$html .= '<script type="text/javascript">' . "\n";
|
170 |
-
$html .= '//<![CDATA[' . "\n";
|
171 |
-
$html .= 'jQuery( document ).ready( function( $ ){' . "\n";
|
172 |
-
$html .= "\t" . '// save screen option' . "\n";
|
173 |
-
$html .= "\t" . '$( "#screen-meta #'. $key .'" )' . "\n";
|
174 |
-
$html .= "\t\t" . '.change(function(){' . "\n";
|
175 |
-
$html .= "\t\t\t" . 'var self = this;' . "\n";
|
176 |
-
$html .= "\t\t\t" . '$.post( ajaxurl, {' . "\n";
|
177 |
-
$html .= "\t\t\t\t" . 'action: "wpof_update_options",' . "\n";
|
178 |
-
$html .= "\t\t\t\t" . '"wpof-nonce": "'. wp_create_nonce( 'wpof_update_options' ) .'",' . "\n";
|
179 |
-
$html .= "\t\t\t\t" . 'ajax_option_name: "'. $this->current_option .'",' . "\n";
|
180 |
-
$html .= "\t\t\t\t" . '"'. $this->field_name( $key ) .'": $( this ).val()' . "\n";
|
181 |
-
$html .= "\t\t\t" . '}, function () {' . "\n";
|
182 |
-
$html .= "\t\t\t\t" . '$( self ).trigger( "ajax_updated" );' . "\n";
|
183 |
-
$html .= "\t\t\t" . '});' . "\n";
|
184 |
-
$html .= "\t\t" . '});' . "\n";
|
185 |
-
$html .= '});' . "\n";
|
186 |
-
$html .= '//]]>' . "\n";
|
187 |
-
$html .= '</script>' . "\n";
|
188 |
-
|
189 |
-
return $html;
|
190 |
-
}
|
191 |
-
|
192 |
-
/**
|
193 |
-
* Get closing form
|
194 |
-
* @return string
|
195 |
-
*/
|
196 |
-
public function close_form() {
|
197 |
-
return '</form>';
|
198 |
-
}
|
199 |
-
|
200 |
-
/**
|
201 |
-
* Text field
|
202 |
-
* @param string $key
|
203 |
-
* @param array $attrs Optional
|
204 |
-
* @return string
|
205 |
-
*/
|
206 |
-
public function text( $key, $attrs = array() ) {
|
207 |
-
if ( ! key_exists( 'class', $attrs ) )
|
208 |
-
$attrs[ 'class' ] = 'regular-text';
|
209 |
-
|
210 |
-
return '<input type="text" '. $this->attrs( $attrs, $key, $this->value( $key ) ) .' />';
|
211 |
-
}
|
212 |
-
|
213 |
-
/**
|
214 |
-
* Text field
|
215 |
-
* @param string $key
|
216 |
-
* @param array $attrs Optional
|
217 |
-
* @return string
|
218 |
-
*/
|
219 |
-
public function textarea( $key, $attrs = array() ) {
|
220 |
-
if ( ! key_exists( 'class', $attrs ) )
|
221 |
-
$attrs[ 'class' ] = 'large-text';
|
222 |
-
|
223 |
-
return '<textarea '. $this->attrs( $attrs, $key ) .'>'. $this->value( $key ) .'</textarea>';
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* Radio field
|
228 |
-
* @param string $key
|
229 |
-
* @param mixed $value
|
230 |
-
* @param array $attrs Optional
|
231 |
-
* @return string
|
232 |
-
*/
|
233 |
-
public function radio( $key, $value, $attrs = array() ) {
|
234 |
-
$checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
|
235 |
-
return '<input type="radio" '. $this->attrs( $attrs, $key, $value )
|
236 |
-
. $checked . ' />';
|
237 |
-
}
|
238 |
-
|
239 |
-
/**
|
240 |
-
* Checkbox field
|
241 |
-
* @param string $key
|
242 |
-
* @param mixed $value
|
243 |
-
* @param array $attrs Optional
|
244 |
-
* @return string
|
245 |
-
*/
|
246 |
-
public function checkbox( $key, $value, $attrs = array() ) {
|
247 |
-
$checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
|
248 |
-
return '<input type="checkbox" '. $this->attrs( $attrs, $key, $value )
|
249 |
-
. $checked . ' />';
|
250 |
-
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* Select field
|
254 |
-
* @param string $key
|
255 |
-
* @param array $options Optional
|
256 |
-
* @param array $attrs Optional
|
257 |
-
* @return string
|
258 |
-
*/
|
259 |
-
public function select( $key, $options = array(), $attrs = array() ) {
|
260 |
-
$html = '<select '. $this->attrs( $attrs, $key ) .'>';
|
261 |
-
|
262 |
-
foreach ( $options AS $value => $label ) {
|
263 |
-
$selected = ( $value == $this->value( $key ) ) ? ' selected="selected"' : '';
|
264 |
-
$html .= '<option value="'. $value .'"'. $selected .'>'. $label .'</option>';
|
265 |
-
}
|
266 |
-
|
267 |
-
$html .= '</select>';
|
268 |
-
return $html;
|
269 |
-
}
|
270 |
-
|
271 |
-
/**
|
272 |
-
* Submit button
|
273 |
-
* @param array $attrs Optional
|
274 |
-
* @return string
|
275 |
-
*/
|
276 |
-
public function submit( $attrs = array() ) {
|
277 |
-
// set class attr
|
278 |
-
$attrs[ 'class' ] = 'button-primary'. ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
|
279 |
-
|
280 |
-
// show submit
|
281 |
-
$html = '';
|
282 |
-
$html .= '<p class="button-controls" style="text-align:right;">';
|
283 |
-
$html .= '<img alt="" title="" class="ajax-feedback" src="'. get_bloginfo( 'url' ) .'/wp-admin/images/wpspin_light.gif" style="visibility: hidden;" />';
|
284 |
-
$html .= '<input type="submit" '. $this->attrs( $attrs, '', __( 'Save Changes' ) ) .' />';
|
285 |
-
$html .= '</p>';
|
286 |
-
return $html;
|
287 |
-
}
|
288 |
-
|
289 |
-
/**
|
290 |
-
* Get field name of given key
|
291 |
-
* @param string $key
|
292 |
-
* @return string
|
293 |
-
*/
|
294 |
-
public function field_name( $key ) {
|
295 |
-
return $this->current_option . '[' . $key . ']';
|
296 |
-
}
|
297 |
-
|
298 |
-
/**
|
299 |
-
* Get value of given option key
|
300 |
-
* @param string $key
|
301 |
-
* @param mixed $default_value Optional
|
302 |
-
* @param boolean $option_name Optional, search in given option_name instead of the current option
|
303 |
-
* @return mixed
|
304 |
-
*/
|
305 |
-
public function value( $key, $default_value = NULL, $option_name = NULL ) {
|
306 |
-
if ( $option_name === NULL ) {
|
307 |
-
$option = $this->current_option;
|
308 |
-
} else {
|
309 |
-
$option = $this->name . '-' . $option_name;
|
310 |
-
}
|
311 |
-
|
312 |
-
if (!isset($this->options[ $option ])) {
|
313 |
-
return $default_value;
|
314 |
-
}
|
315 |
-
|
316 |
-
$values = $this->options[ $option ];
|
317 |
-
|
318 |
-
return ( is_array( $values ) AND key_exists( $key, $values ) AND $values[ $key ] !== NULL ) ? $values[ $key ] : $default_value;
|
319 |
-
}
|
320 |
-
|
321 |
-
/**
|
322 |
-
* Delete and unregister option
|
323 |
-
*/
|
324 |
-
public function delete_options() {
|
325 |
-
foreach ( $this->options AS $option_name => $values ) {
|
326 |
-
delete_option( $option_name );
|
327 |
-
}
|
328 |
-
}
|
329 |
-
|
330 |
-
/**
|
331 |
-
* Get string of given attributes
|
332 |
-
* @param array $attrs
|
333 |
-
* @param string $key Optional
|
334 |
-
* @param mixed $value Optional
|
335 |
-
* @return string
|
336 |
-
*/
|
337 |
-
protected function attrs( $attrs, $key = NULL, $value = NULL ) {
|
338 |
-
$str = '';
|
339 |
-
|
340 |
-
// set name, id, value attr
|
341 |
-
if ( $key !== NULL ) {
|
342 |
-
$str .= 'name="' . $this->field_name( $key ) .'" ';
|
343 |
-
if ( ! key_exists( 'id', $attrs ) )
|
344 |
-
$str .= 'id="' . $key .'" ';
|
345 |
-
}
|
346 |
-
|
347 |
-
if ( $value !== NULL )
|
348 |
-
$str .= 'value="' . $value .'" ';
|
349 |
-
|
350 |
-
foreach ( $attrs AS $attr => $value )
|
351 |
-
$str .= $attr .'="'. $value .'" ';
|
352 |
-
|
353 |
-
return $str;
|
354 |
-
}
|
355 |
-
|
356 |
-
} // End WP_Option_Forms_01
|
357 |
-
|
358 |
-
endif;
|
359 |
-
|
360 |
-
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
1 |
+
<?php defined( 'ABSPATH' ) OR die( 'No direct access.' );
|
2 |
+
if ( ! class_exists( 'WP_Option_Forms_01' ) ):
|
3 |
+
/**
|
4 |
+
* WP_Option_Forms_01 Class
|
5 |
+
*
|
6 |
+
* Simple class for creating option forms of a the same option_group.
|
7 |
+
* Also with Ajax save support.
|
8 |
+
*
|
9 |
+
* Requires WordPress 3.0+ and PHP 5.2+
|
10 |
+
*
|
11 |
+
* @version 0.1
|
12 |
+
* @author Victor Villaverde Laan
|
13 |
+
* @link http://www.freelancephp.net/
|
14 |
+
* @license Dual licensed under the MIT and GPL licenses
|
15 |
+
*/
|
16 |
+
class WP_Option_Forms_01 {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Name used as prefix for saving option names
|
20 |
+
* @var string
|
21 |
+
*/
|
22 |
+
protected $name = NULL;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Option names and values
|
26 |
+
* @var string
|
27 |
+
*/
|
28 |
+
protected $options = array();
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Current option name
|
32 |
+
* @var string
|
33 |
+
*/
|
34 |
+
protected $current_option = NULL;
|
35 |
+
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Constructor
|
39 |
+
* @param array $name
|
40 |
+
* @param array $options Optional
|
41 |
+
*/
|
42 |
+
public function __construct( $name, $options = array() ) {
|
43 |
+
$this->name = sanitize_title_with_dashes( $name );
|
44 |
+
|
45 |
+
// set option names
|
46 |
+
foreach ( $options AS $option_name => $values ) {
|
47 |
+
$this->add_option( $option_name, $values );
|
48 |
+
}
|
49 |
+
|
50 |
+
// actions
|
51 |
+
add_action( 'wp_ajax_wpof_update_options', array( $this, 'call_wp_ajax' ) );
|
52 |
+
add_action( 'admin_menu', array( $this, 'call_admin_menu' ) );
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Admin menu callback
|
57 |
+
*/
|
58 |
+
public function call_admin_menu() {
|
59 |
+
// Register settings
|
60 |
+
foreach ( $this->options AS $option_name => $values ) {
|
61 |
+
register_setting( $option_name, $option_name );
|
62 |
+
}
|
63 |
+
|
64 |
+
// script
|
65 |
+
// wp_enqueue_script( 'option-forms', plugins_url( 'js/wp-option-forms.js', WP_EXTERNAL_LINKS_FILE ), array( 'jquery' ), '1.0' );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Ajax call for saving option values
|
70 |
+
*/
|
71 |
+
public function call_wp_ajax() {
|
72 |
+
check_ajax_referer( 'wpof_update_options', 'wpof-nonce' );
|
73 |
+
|
74 |
+
$option_name = $_POST[ 'ajax_option_name' ];
|
75 |
+
$value = NULL;
|
76 |
+
|
77 |
+
if ( isset( $_POST[ $option_name ] ) )
|
78 |
+
$value = $_POST[ $option_name ];
|
79 |
+
|
80 |
+
if ( ! is_array( $value ) )
|
81 |
+
$value = trim( $value );
|
82 |
+
|
83 |
+
$value = stripslashes_deep( $value );
|
84 |
+
|
85 |
+
update_option( $option_name, $value );
|
86 |
+
|
87 |
+
die( '1' );
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Add option (or reset option when already exists)
|
92 |
+
* @param string $option_name
|
93 |
+
* @param array $default_values Optional
|
94 |
+
* @return this
|
95 |
+
*/
|
96 |
+
public function add_option( $option_name, $default_values = array() ) {
|
97 |
+
// set values
|
98 |
+
$saved_values = get_option( $this->name .'-'. $option_name );
|
99 |
+
|
100 |
+
if ( empty( $saved_values ) ) {
|
101 |
+
foreach ( $default_values AS $key => $value )
|
102 |
+
$values[ $key ] = $value;
|
103 |
+
} else {
|
104 |
+
foreach ( $default_values AS $key => $value )
|
105 |
+
$values[ $key ] = '';
|
106 |
+
|
107 |
+
foreach ( $saved_values AS $key => $value )
|
108 |
+
$values[ $key ] = $value;
|
109 |
+
}
|
110 |
+
|
111 |
+
// option and values
|
112 |
+
$this->options[ $this->name .'-'. $option_name ] = $values;
|
113 |
+
return $this;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Set current option to use
|
118 |
+
* @param string $option_name
|
119 |
+
* @return this
|
120 |
+
*/
|
121 |
+
public function set_current_option( $option_name ) {
|
122 |
+
$this->current_option = $this->name .'-'. $option_name;
|
123 |
+
return $this;
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Get opening form with all nescessary WP fields
|
128 |
+
* @param boolean $ajaxSave Optional
|
129 |
+
* @param array $attrs Optional
|
130 |
+
* @return string
|
131 |
+
*/
|
132 |
+
public function open_form( $ajaxSave = TRUE, $attrs = array() ) {
|
133 |
+
// set class for ajax or non-ajax form
|
134 |
+
$attrs[ 'class' ] = ( ( $ajaxSave ) ? 'ajax-form' : 'no-ajax-form' )
|
135 |
+
. ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
|
136 |
+
|
137 |
+
// show start form
|
138 |
+
$html = '';
|
139 |
+
$html .= '<form method="post" action="options.php" '. $this->attrs( $attrs ) .'>';
|
140 |
+
|
141 |
+
if ( $ajaxSave ) {
|
142 |
+
$html .= wp_nonce_field( 'wpof_update_options', 'wpof-nonce', FALSE, FALSE );
|
143 |
+
$html .= '<input type="hidden" name="action" value="wpof_update_options" />';
|
144 |
+
$html .= '<input type="hidden" name="ajax_option_name" value="'. $this->current_option .'" />';
|
145 |
+
|
146 |
+
// instead of using settings_fields();
|
147 |
+
$html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
|
148 |
+
$html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
|
149 |
+
} else {
|
150 |
+
// instead of using settings_fields();
|
151 |
+
$html .= '<input type="hidden" name="option_page" value="' . esc_attr( $this->current_option ) . '" />';
|
152 |
+
$html .= '<input type="hidden" name="action" value="update" />';
|
153 |
+
$html .= wp_nonce_field( $this->current_option . '-options', '_wpnonce', TRUE, FALSE );
|
154 |
+
}
|
155 |
+
|
156 |
+
return $html;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Get script for saving screen option
|
161 |
+
* @param string $option_name
|
162 |
+
* @param string $key
|
163 |
+
* @return string
|
164 |
+
*/
|
165 |
+
public function open_screen_option( $option_name, $key ) {
|
166 |
+
$this->set_current_option( $option_name );
|
167 |
+
|
168 |
+
$html = '';
|
169 |
+
$html .= '<script type="text/javascript">' . "\n";
|
170 |
+
$html .= '//<![CDATA[' . "\n";
|
171 |
+
$html .= 'jQuery( document ).ready( function( $ ){' . "\n";
|
172 |
+
$html .= "\t" . '// save screen option' . "\n";
|
173 |
+
$html .= "\t" . '$( "#screen-meta #'. $key .'" )' . "\n";
|
174 |
+
$html .= "\t\t" . '.change(function(){' . "\n";
|
175 |
+
$html .= "\t\t\t" . 'var self = this;' . "\n";
|
176 |
+
$html .= "\t\t\t" . '$.post( ajaxurl, {' . "\n";
|
177 |
+
$html .= "\t\t\t\t" . 'action: "wpof_update_options",' . "\n";
|
178 |
+
$html .= "\t\t\t\t" . '"wpof-nonce": "'. wp_create_nonce( 'wpof_update_options' ) .'",' . "\n";
|
179 |
+
$html .= "\t\t\t\t" . 'ajax_option_name: "'. $this->current_option .'",' . "\n";
|
180 |
+
$html .= "\t\t\t\t" . '"'. $this->field_name( $key ) .'": $( this ).val()' . "\n";
|
181 |
+
$html .= "\t\t\t" . '}, function () {' . "\n";
|
182 |
+
$html .= "\t\t\t\t" . '$( self ).trigger( "ajax_updated" );' . "\n";
|
183 |
+
$html .= "\t\t\t" . '});' . "\n";
|
184 |
+
$html .= "\t\t" . '});' . "\n";
|
185 |
+
$html .= '});' . "\n";
|
186 |
+
$html .= '//]]>' . "\n";
|
187 |
+
$html .= '</script>' . "\n";
|
188 |
+
|
189 |
+
return $html;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Get closing form
|
194 |
+
* @return string
|
195 |
+
*/
|
196 |
+
public function close_form() {
|
197 |
+
return '</form>';
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Text field
|
202 |
+
* @param string $key
|
203 |
+
* @param array $attrs Optional
|
204 |
+
* @return string
|
205 |
+
*/
|
206 |
+
public function text( $key, $attrs = array() ) {
|
207 |
+
if ( ! key_exists( 'class', $attrs ) )
|
208 |
+
$attrs[ 'class' ] = 'regular-text';
|
209 |
+
|
210 |
+
return '<input type="text" '. $this->attrs( $attrs, $key, $this->value( $key ) ) .' />';
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Text field
|
215 |
+
* @param string $key
|
216 |
+
* @param array $attrs Optional
|
217 |
+
* @return string
|
218 |
+
*/
|
219 |
+
public function textarea( $key, $attrs = array() ) {
|
220 |
+
if ( ! key_exists( 'class', $attrs ) )
|
221 |
+
$attrs[ 'class' ] = 'large-text';
|
222 |
+
|
223 |
+
return '<textarea '. $this->attrs( $attrs, $key ) .'>'. $this->value( $key ) .'</textarea>';
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Radio field
|
228 |
+
* @param string $key
|
229 |
+
* @param mixed $value
|
230 |
+
* @param array $attrs Optional
|
231 |
+
* @return string
|
232 |
+
*/
|
233 |
+
public function radio( $key, $value, $attrs = array() ) {
|
234 |
+
$checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
|
235 |
+
return '<input type="radio" '. $this->attrs( $attrs, $key, $value )
|
236 |
+
. $checked . ' />';
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Checkbox field
|
241 |
+
* @param string $key
|
242 |
+
* @param mixed $value
|
243 |
+
* @param array $attrs Optional
|
244 |
+
* @return string
|
245 |
+
*/
|
246 |
+
public function checkbox( $key, $value, $attrs = array() ) {
|
247 |
+
$checked = ( $value == $this->value( $key ) ) ? ' checked="checked"' : '';
|
248 |
+
return '<input type="checkbox" '. $this->attrs( $attrs, $key, $value )
|
249 |
+
. $checked . ' />';
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Select field
|
254 |
+
* @param string $key
|
255 |
+
* @param array $options Optional
|
256 |
+
* @param array $attrs Optional
|
257 |
+
* @return string
|
258 |
+
*/
|
259 |
+
public function select( $key, $options = array(), $attrs = array() ) {
|
260 |
+
$html = '<select '. $this->attrs( $attrs, $key ) .'>';
|
261 |
+
|
262 |
+
foreach ( $options AS $value => $label ) {
|
263 |
+
$selected = ( $value == $this->value( $key ) ) ? ' selected="selected"' : '';
|
264 |
+
$html .= '<option value="'. $value .'"'. $selected .'>'. $label .'</option>';
|
265 |
+
}
|
266 |
+
|
267 |
+
$html .= '</select>';
|
268 |
+
return $html;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Submit button
|
273 |
+
* @param array $attrs Optional
|
274 |
+
* @return string
|
275 |
+
*/
|
276 |
+
public function submit( $attrs = array() ) {
|
277 |
+
// set class attr
|
278 |
+
$attrs[ 'class' ] = 'button-primary'. ( ( key_exists( 'class', $attrs ) ) ? ' '. $attrs[ 'class' ] : '' );
|
279 |
+
|
280 |
+
// show submit
|
281 |
+
$html = '';
|
282 |
+
$html .= '<p class="button-controls" style="text-align:right;">';
|
283 |
+
$html .= '<img alt="" title="" class="ajax-feedback" src="'. get_bloginfo( 'url' ) .'/wp-admin/images/wpspin_light.gif" style="visibility: hidden;" />';
|
284 |
+
$html .= '<input type="submit" '. $this->attrs( $attrs, '', __( 'Save Changes' ) ) .' />';
|
285 |
+
$html .= '</p>';
|
286 |
+
return $html;
|
287 |
+
}
|
288 |
+
|
289 |
+
/**
|
290 |
+
* Get field name of given key
|
291 |
+
* @param string $key
|
292 |
+
* @return string
|
293 |
+
*/
|
294 |
+
public function field_name( $key ) {
|
295 |
+
return $this->current_option . '[' . $key . ']';
|
296 |
+
}
|
297 |
+
|
298 |
+
/**
|
299 |
+
* Get value of given option key
|
300 |
+
* @param string $key
|
301 |
+
* @param mixed $default_value Optional
|
302 |
+
* @param boolean $option_name Optional, search in given option_name instead of the current option
|
303 |
+
* @return mixed
|
304 |
+
*/
|
305 |
+
public function value( $key, $default_value = NULL, $option_name = NULL ) {
|
306 |
+
if ( $option_name === NULL ) {
|
307 |
+
$option = $this->current_option;
|
308 |
+
} else {
|
309 |
+
$option = $this->name . '-' . $option_name;
|
310 |
+
}
|
311 |
+
|
312 |
+
if (!isset($this->options[ $option ])) {
|
313 |
+
return $default_value;
|
314 |
+
}
|
315 |
+
|
316 |
+
$values = $this->options[ $option ];
|
317 |
+
|
318 |
+
return ( is_array( $values ) AND key_exists( $key, $values ) AND $values[ $key ] !== NULL ) ? $values[ $key ] : $default_value;
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Delete and unregister option
|
323 |
+
*/
|
324 |
+
public function delete_options() {
|
325 |
+
foreach ( $this->options AS $option_name => $values ) {
|
326 |
+
delete_option( $option_name );
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Get string of given attributes
|
332 |
+
* @param array $attrs
|
333 |
+
* @param string $key Optional
|
334 |
+
* @param mixed $value Optional
|
335 |
+
* @return string
|
336 |
+
*/
|
337 |
+
protected function attrs( $attrs, $key = NULL, $value = NULL ) {
|
338 |
+
$str = '';
|
339 |
+
|
340 |
+
// set name, id, value attr
|
341 |
+
if ( $key !== NULL ) {
|
342 |
+
$str .= 'name="' . $this->field_name( $key ) .'" ';
|
343 |
+
if ( ! key_exists( 'id', $attrs ) )
|
344 |
+
$str .= 'id="' . $key .'" ';
|
345 |
+
}
|
346 |
+
|
347 |
+
if ( $value !== NULL )
|
348 |
+
$str .= 'value="' . $value .'" ';
|
349 |
+
|
350 |
+
foreach ( $attrs AS $attr => $value )
|
351 |
+
$str .= $attr .'="'. $value .'" ';
|
352 |
+
|
353 |
+
return $str;
|
354 |
+
}
|
355 |
+
|
356 |
+
} // End WP_Option_Forms_01
|
357 |
+
|
358 |
+
endif;
|
359 |
+
|
360 |
+
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|
js/admin-wp-external-links.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
/* WP External Links - Admin */
|
2 |
-
jQuery(function(e){"use strict";(function(){e.fn.tipsy=function(t){t=e.extend({},e.fn.tipsy.defaults,t);return this.each(function(){var n=e.fn.tipsy.elementOptions(this,t);e(this).hover(function(){e.data(this,"cancel.tipsy",true);var t=e.data(this,"active.tipsy");if(!t){t=e('<div class="tipsy"><div class="tipsy-inner"/></div>');t.css({position:"absolute",zIndex:1e5});e.data(this,"active.tipsy",t)}if(e(this).attr("title")||typeof e(this).attr("original-title")!=="string"){e(this).attr("original-title",e(this).attr("title")||"").removeAttr("title")}var r;if(typeof n.title==="string"){r=e(this).attr(n.title==="title"?"original-title":n.title)}else if(typeof n.title==="function"){r=n.title.call(this)}t.find(".tipsy-inner")[n.html?"html":"text"](r||n.fallback);var i=e.extend({},e(this).offset(),{width:this.offsetWidth,height:this.offsetHeight});t.get(0).className="tipsy";t.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).appendTo(document.body);var s=t[0].offsetWidth,o=t[0].offsetHeight;var u=typeof n.gravity=="function"?n.gravity.call(this):n.gravity;switch(u.charAt(0)){case"n":t.css({top:i.top+i.height,left:i.left+i.width/2-s/2}).addClass("tipsy-north");break;case"s":t.css({top:i.top-o,left:i.left+i.width/2-s/2}).addClass("tipsy-south");break;case"e":t.css({top:i.top+i.height/2-o/2,left:i.left-s}).addClass("tipsy-east");break;case"w":t.css({top:i.top+i.height/2-o/2,left:i.left+i.width}).addClass("tipsy-west");break}if(n.fade){t.css({opacity:0,display:"block",visibility:"visible"}).animate({opacity:.9})}else{t.css({visibility:"visible"})}},function(){e.data(this,"cancel.tipsy",false);var t=this;setTimeout(function(){if(e.data(this,"cancel.tipsy"))return;var r=e.data(t,"active.tipsy");if(n.fade){r.stop().fadeOut(function(){e(this).remove()})}else{r.remove()}},100)})})};e.fn.tipsy.elementOptions=function(t,n){return e.metadata?e.extend({},n,e(t).metadata()):n};e.fn.tipsy.defaults={fade:false,fallback:"",gravity:"w",html:false,title:"title"};e.fn.tipsy.autoNS=function(){return e(this).offset().top>e(document).scrollTop()+e(window).height()/2?"s":"n"};e.fn.tipsy.autoWE=function(){return e(this).offset().left>e(document).scrollLeft()+e(window).width()/2?"e":"w"}})();e("#setting-error-settings_updated").click(function(){e(this).hide()});e("input#filter_page").change(function(){var t=e("input#filter_posts, input#filter_comments, input#filter_widgets");if(e(this).attr("checked")){t.attr("disabled",true).attr("checked",true)}else{t.attr("disabled",false)}}).change();e("input#use_js").change(function(){var t=e("input#load_in_footer");if(e(this).attr("checked")){t.attr("disabled",false)}else{t.attr("disabled",true).attr("checked",false)}}).change();e("input#phpquery").change(function(){if(e(this).attr("checked")){e(".filter_excl_sel").fadeIn()}else{e(".filter_excl_sel").fadeOut()}}).change();e("#menu_position").parents("form.ajax-form").on("ajax_saved_options",function(){var t=e(this).val()||"";window.location.href=t+(t.indexOf("?")>-1?"&":"?")+"page=wp_external_links&settings-updated=true"});e(".tooltip-help").css("margin","0 5px").tipsy({fade:true,live:true,gravity:"w",fallback:"No help text."});e('*[type="submit"]').removeClass("submit");e(".postbox").find(".handlediv, .hndle").click(function(){var t=e(this).parent().find(".inside");if(t.css("display")==="block"){t.css({display:"none"})}else{t.css({display:"block"})}})});jQuery(function(e){"use strict";var t=function(t){var n=e(t),r=n.parents("form"),i=r.serializeArray();n.attr("disabled",true);r.find(".ajax-feedback").css("visibility","visible");e.post(ajaxurl,i,function(t){var i=e("<strong>").insertBefore(n);if(t==="1"){i.html("Saved")}else{r.find('[name="action"]').val("update");r.submit()}i.css({margin:"0 5px"}).delay(1e3).fadeOut(function(){e(this).remove()});n.attr("disabled",false);r.find(".ajax-feedback").css("visibility","hidden");r.trigger("ajax_saved_options",[t])})};e('form.ajax-form input[type="submit"]').click(function(e){t(this);e.preventDefault()})})
|
1 |
+
/* WP External Links - Admin */
|
2 |
+
jQuery(function(e){"use strict";(function(){e.fn.tipsy=function(t){t=e.extend({},e.fn.tipsy.defaults,t);return this.each(function(){var n=e.fn.tipsy.elementOptions(this,t);e(this).hover(function(){e.data(this,"cancel.tipsy",true);var t=e.data(this,"active.tipsy");if(!t){t=e('<div class="tipsy"><div class="tipsy-inner"/></div>');t.css({position:"absolute",zIndex:1e5});e.data(this,"active.tipsy",t)}if(e(this).attr("title")||typeof e(this).attr("original-title")!=="string"){e(this).attr("original-title",e(this).attr("title")||"").removeAttr("title")}var r;if(typeof n.title==="string"){r=e(this).attr(n.title==="title"?"original-title":n.title)}else if(typeof n.title==="function"){r=n.title.call(this)}t.find(".tipsy-inner")[n.html?"html":"text"](r||n.fallback);var i=e.extend({},e(this).offset(),{width:this.offsetWidth,height:this.offsetHeight});t.get(0).className="tipsy";t.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).appendTo(document.body);var s=t[0].offsetWidth,o=t[0].offsetHeight;var u=typeof n.gravity=="function"?n.gravity.call(this):n.gravity;switch(u.charAt(0)){case"n":t.css({top:i.top+i.height,left:i.left+i.width/2-s/2}).addClass("tipsy-north");break;case"s":t.css({top:i.top-o,left:i.left+i.width/2-s/2}).addClass("tipsy-south");break;case"e":t.css({top:i.top+i.height/2-o/2,left:i.left-s}).addClass("tipsy-east");break;case"w":t.css({top:i.top+i.height/2-o/2,left:i.left+i.width}).addClass("tipsy-west");break}if(n.fade){t.css({opacity:0,display:"block",visibility:"visible"}).animate({opacity:.9})}else{t.css({visibility:"visible"})}},function(){e.data(this,"cancel.tipsy",false);var t=this;setTimeout(function(){if(e.data(this,"cancel.tipsy"))return;var r=e.data(t,"active.tipsy");if(n.fade){r.stop().fadeOut(function(){e(this).remove()})}else{r.remove()}},100)})})};e.fn.tipsy.elementOptions=function(t,n){return e.metadata?e.extend({},n,e(t).metadata()):n};e.fn.tipsy.defaults={fade:false,fallback:"",gravity:"w",html:false,title:"title"};e.fn.tipsy.autoNS=function(){return e(this).offset().top>e(document).scrollTop()+e(window).height()/2?"s":"n"};e.fn.tipsy.autoWE=function(){return e(this).offset().left>e(document).scrollLeft()+e(window).width()/2?"e":"w"}})();e("#setting-error-settings_updated").click(function(){e(this).hide()});e("input#filter_page").change(function(){var t=e("input#filter_posts, input#filter_comments, input#filter_widgets");if(e(this).attr("checked")){t.attr("disabled",true).attr("checked",true)}else{t.attr("disabled",false)}}).change();e("input#use_js").change(function(){var t=e("input#load_in_footer");if(e(this).attr("checked")){t.attr("disabled",false)}else{t.attr("disabled",true).attr("checked",false)}}).change();e("input#phpquery").change(function(){if(e(this).attr("checked")){e(".filter_excl_sel").fadeIn()}else{e(".filter_excl_sel").fadeOut()}}).change();e("#menu_position").parents("form.ajax-form").on("ajax_saved_options",function(){var t=e(this).val()||"";window.location.href=t+(t.indexOf("?")>-1?"&":"?")+"page=wp_external_links&settings-updated=true"});e(".tooltip-help").css("margin","0 5px").tipsy({fade:true,live:true,gravity:"w",fallback:"No help text."});e('*[type="submit"]').removeClass("submit");e(".postbox").find(".handlediv, .hndle").click(function(){var t=e(this).parent().find(".inside");if(t.css("display")==="block"){t.css({display:"none"})}else{t.css({display:"block"})}})});jQuery(function(e){"use strict";var t=function(t){var n=e(t),r=n.parents("form"),i=r.serializeArray();n.attr("disabled",true);r.find(".ajax-feedback").css("visibility","visible");e.post(ajaxurl,i,function(t){var i=e("<strong>").insertBefore(n);if(t==="1"){i.html("Saved")}else{r.find('[name="action"]').val("update");r.submit()}i.css({margin:"0 5px"}).delay(1e3).fadeOut(function(){e(this).remove()});n.attr("disabled",false);r.find(".ajax-feedback").css("visibility","hidden");r.trigger("ajax_saved_options",[t])})};e('form.ajax-form input[type="submit"]').click(function(e){t(this);e.preventDefault()})})
|
js/src/.htaccess
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
deny from all
|
js/src/admin-wp-external-links.js
CHANGED
@@ -1,173 +1,173 @@
|
|
1 |
-
/* WP External Links - Admin */
|
2 |
-
/*global jQuery, window*/
|
3 |
-
jQuery(function ($) {
|
4 |
-
'use strict';
|
5 |
-
|
6 |
-
/* Tipsy Plugin */
|
7 |
-
(function () {
|
8 |
-
$.fn.tipsy = function (options) {
|
9 |
-
options = $.extend({}, $.fn.tipsy.defaults, options);
|
10 |
-
|
11 |
-
return this.each(function () {
|
12 |
-
var opts = $.fn.tipsy.elementOptions(this, options);
|
13 |
-
|
14 |
-
$(this).hover(function () {
|
15 |
-
$.data(this, 'cancel.tipsy', true);
|
16 |
-
|
17 |
-
var tip = $.data(this, 'active.tipsy');
|
18 |
-
if (!tip) {
|
19 |
-
tip = $('<div class="tipsy"><div class="tipsy-inner"/></div>');
|
20 |
-
tip.css({position: 'absolute', zIndex: 100000});
|
21 |
-
$.data(this, 'active.tipsy', tip);
|
22 |
-
}
|
23 |
-
|
24 |
-
if ($(this).attr('title') || typeof $(this).attr('original-title') !== 'string') {
|
25 |
-
$(this).attr('original-title', $(this).attr('title') || '').removeAttr('title');
|
26 |
-
}
|
27 |
-
|
28 |
-
var title;
|
29 |
-
if (typeof opts.title === 'string') {
|
30 |
-
title = $(this).attr(opts.title === 'title' ? 'original-title' : opts.title);
|
31 |
-
} else if (typeof opts.title === 'function') {
|
32 |
-
title = opts.title.call(this);
|
33 |
-
}
|
34 |
-
|
35 |
-
tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
|
36 |
-
|
37 |
-
var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight});
|
38 |
-
tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity
|
39 |
-
tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
|
40 |
-
var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
|
41 |
-
var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity;
|
42 |
-
|
43 |
-
switch (gravity.charAt(0)) {
|
44 |
-
case 'n':
|
45 |
-
tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
|
46 |
-
break;
|
47 |
-
case 's':
|
48 |
-
tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
|
49 |
-
break;
|
50 |
-
case 'e':
|
51 |
-
tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
|
52 |
-
break;
|
53 |
-
case 'w':
|
54 |
-
tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
|
55 |
-
break;
|
56 |
-
}
|
57 |
-
|
58 |
-
if (opts.fade) {
|
59 |
-
tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 0.9});
|
60 |
-
} else {
|
61 |
-
tip.css({visibility: 'visible'});
|
62 |
-
}
|
63 |
-
|
64 |
-
}, function () {
|
65 |
-
$.data(this, 'cancel.tipsy', false);
|
66 |
-
var self = this;
|
67 |
-
setTimeout(function () {
|
68 |
-
if ($.data(this, 'cancel.tipsy')) return;
|
69 |
-
var tip = $.data(self, 'active.tipsy');
|
70 |
-
if (opts.fade) {
|
71 |
-
tip.stop().fadeOut(function () { $(this).remove(); });
|
72 |
-
} else {
|
73 |
-
tip.remove();
|
74 |
-
}
|
75 |
-
}, 100);
|
76 |
-
});
|
77 |
-
});
|
78 |
-
};
|
79 |
-
|
80 |
-
// Overwrite this method to provide options on a per-element basis.
|
81 |
-
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
82 |
-
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
83 |
-
// (remember - do not modify 'options' in place!)
|
84 |
-
$.fn.tipsy.elementOptions = function (ele, options) {
|
85 |
-
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
86 |
-
};
|
87 |
-
|
88 |
-
$.fn.tipsy.defaults = {
|
89 |
-
fade: false,
|
90 |
-
fallback: '',
|
91 |
-
gravity: 'w',
|
92 |
-
html: false,
|
93 |
-
title: 'title'
|
94 |
-
};
|
95 |
-
|
96 |
-
$.fn.tipsy.autoNS = function () {
|
97 |
-
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
98 |
-
};
|
99 |
-
|
100 |
-
$.fn.tipsy.autoWE = function () {
|
101 |
-
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
102 |
-
};
|
103 |
-
|
104 |
-
})(); // End Tipsy Plugin
|
105 |
-
|
106 |
-
|
107 |
-
$('#setting-error-settings_updated').click(function () {
|
108 |
-
$(this).hide();
|
109 |
-
});
|
110 |
-
|
111 |
-
// option filter page
|
112 |
-
$('input#filter_page')
|
113 |
-
.change(function () {
|
114 |
-
var $i = $('input#filter_posts, input#filter_comments, input#filter_widgets');
|
115 |
-
|
116 |
-
if ($(this).attr('checked')) {
|
117 |
-
$i.attr('disabled', true)
|
118 |
-
.attr('checked', true);
|
119 |
-
} else {
|
120 |
-
$i.attr('disabled', false);
|
121 |
-
}
|
122 |
-
})
|
123 |
-
.change();
|
124 |
-
|
125 |
-
// option use js
|
126 |
-
$('input#use_js')
|
127 |
-
.change(function () {
|
128 |
-
var $i = $('input#load_in_footer');
|
129 |
-
|
130 |
-
if ($(this).attr('checked')) {
|
131 |
-
$i.attr('disabled', false);
|
132 |
-
} else {
|
133 |
-
$i.attr('disabled', true)
|
134 |
-
.attr('checked', false);
|
135 |
-
}
|
136 |
-
})
|
137 |
-
.change();
|
138 |
-
|
139 |
-
// option filter_excl_sel
|
140 |
-
$('input#phpquery')
|
141 |
-
.change(function () {
|
142 |
-
if ($(this).attr('checked')) {
|
143 |
-
$('.filter_excl_sel').fadeIn();
|
144 |
-
} else {
|
145 |
-
$('.filter_excl_sel').fadeOut();
|
146 |
-
}
|
147 |
-
})
|
148 |
-
.change();
|
149 |
-
|
150 |
-
// refresh page when updated menu position
|
151 |
-
$('#menu_position').parents('form.ajax-form').on('ajax_saved_options', function () {
|
152 |
-
var s = $(this).val() || '';
|
153 |
-
window.location.href = s + (s.indexOf('?') > -1 ? '&' : '?') + 'page=wp_external_links&settings-updated=true';
|
154 |
-
});
|
155 |
-
|
156 |
-
// set tooltips
|
157 |
-
$('.tooltip-help').css('margin', '0 5px').tipsy({ fade: true, live: true, gravity: 'w', fallback: 'No help text.' });
|
158 |
-
|
159 |
-
// remove class to fix button background
|
160 |
-
$('*[type="submit"]').removeClass('submit');
|
161 |
-
|
162 |
-
// slide postbox
|
163 |
-
$('.postbox').find('.handlediv, .hndle').click(function () {
|
164 |
-
var $inside = $(this).parent().find('.inside');
|
165 |
-
|
166 |
-
if ($inside.css('display') === 'block') {
|
167 |
-
$inside.css({ display: 'none' });
|
168 |
-
} else {
|
169 |
-
$inside.css({ display: 'block' });
|
170 |
-
}
|
171 |
-
});
|
172 |
-
|
173 |
-
});
|
1 |
+
/* WP External Links - Admin */
|
2 |
+
/*global jQuery, window*/
|
3 |
+
jQuery(function ($) {
|
4 |
+
'use strict';
|
5 |
+
|
6 |
+
/* Tipsy Plugin */
|
7 |
+
(function () {
|
8 |
+
$.fn.tipsy = function (options) {
|
9 |
+
options = $.extend({}, $.fn.tipsy.defaults, options);
|
10 |
+
|
11 |
+
return this.each(function () {
|
12 |
+
var opts = $.fn.tipsy.elementOptions(this, options);
|
13 |
+
|
14 |
+
$(this).hover(function () {
|
15 |
+
$.data(this, 'cancel.tipsy', true);
|
16 |
+
|
17 |
+
var tip = $.data(this, 'active.tipsy');
|
18 |
+
if (!tip) {
|
19 |
+
tip = $('<div class="tipsy"><div class="tipsy-inner"/></div>');
|
20 |
+
tip.css({position: 'absolute', zIndex: 100000});
|
21 |
+
$.data(this, 'active.tipsy', tip);
|
22 |
+
}
|
23 |
+
|
24 |
+
if ($(this).attr('title') || typeof $(this).attr('original-title') !== 'string') {
|
25 |
+
$(this).attr('original-title', $(this).attr('title') || '').removeAttr('title');
|
26 |
+
}
|
27 |
+
|
28 |
+
var title;
|
29 |
+
if (typeof opts.title === 'string') {
|
30 |
+
title = $(this).attr(opts.title === 'title' ? 'original-title' : opts.title);
|
31 |
+
} else if (typeof opts.title === 'function') {
|
32 |
+
title = opts.title.call(this);
|
33 |
+
}
|
34 |
+
|
35 |
+
tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback);
|
36 |
+
|
37 |
+
var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight});
|
38 |
+
tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity
|
39 |
+
tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
|
40 |
+
var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
|
41 |
+
var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity;
|
42 |
+
|
43 |
+
switch (gravity.charAt(0)) {
|
44 |
+
case 'n':
|
45 |
+
tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
|
46 |
+
break;
|
47 |
+
case 's':
|
48 |
+
tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
|
49 |
+
break;
|
50 |
+
case 'e':
|
51 |
+
tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
|
52 |
+
break;
|
53 |
+
case 'w':
|
54 |
+
tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
|
55 |
+
break;
|
56 |
+
}
|
57 |
+
|
58 |
+
if (opts.fade) {
|
59 |
+
tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 0.9});
|
60 |
+
} else {
|
61 |
+
tip.css({visibility: 'visible'});
|
62 |
+
}
|
63 |
+
|
64 |
+
}, function () {
|
65 |
+
$.data(this, 'cancel.tipsy', false);
|
66 |
+
var self = this;
|
67 |
+
setTimeout(function () {
|
68 |
+
if ($.data(this, 'cancel.tipsy')) return;
|
69 |
+
var tip = $.data(self, 'active.tipsy');
|
70 |
+
if (opts.fade) {
|
71 |
+
tip.stop().fadeOut(function () { $(this).remove(); });
|
72 |
+
} else {
|
73 |
+
tip.remove();
|
74 |
+
}
|
75 |
+
}, 100);
|
76 |
+
});
|
77 |
+
});
|
78 |
+
};
|
79 |
+
|
80 |
+
// Overwrite this method to provide options on a per-element basis.
|
81 |
+
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
82 |
+
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
83 |
+
// (remember - do not modify 'options' in place!)
|
84 |
+
$.fn.tipsy.elementOptions = function (ele, options) {
|
85 |
+
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
86 |
+
};
|
87 |
+
|
88 |
+
$.fn.tipsy.defaults = {
|
89 |
+
fade: false,
|
90 |
+
fallback: '',
|
91 |
+
gravity: 'w',
|
92 |
+
html: false,
|
93 |
+
title: 'title'
|
94 |
+
};
|
95 |
+
|
96 |
+
$.fn.tipsy.autoNS = function () {
|
97 |
+
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
98 |
+
};
|
99 |
+
|
100 |
+
$.fn.tipsy.autoWE = function () {
|
101 |
+
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
102 |
+
};
|
103 |
+
|
104 |
+
})(); // End Tipsy Plugin
|
105 |
+
|
106 |
+
|
107 |
+
$('#setting-error-settings_updated').click(function () {
|
108 |
+
$(this).hide();
|
109 |
+
});
|
110 |
+
|
111 |
+
// option filter page
|
112 |
+
$('input#filter_page')
|
113 |
+
.change(function () {
|
114 |
+
var $i = $('input#filter_posts, input#filter_comments, input#filter_widgets');
|
115 |
+
|
116 |
+
if ($(this).attr('checked')) {
|
117 |
+
$i.attr('disabled', true)
|
118 |
+
.attr('checked', true);
|
119 |
+
} else {
|
120 |
+
$i.attr('disabled', false);
|
121 |
+
}
|
122 |
+
})
|
123 |
+
.change();
|
124 |
+
|
125 |
+
// option use js
|
126 |
+
$('input#use_js')
|
127 |
+
.change(function () {
|
128 |
+
var $i = $('input#load_in_footer');
|
129 |
+
|
130 |
+
if ($(this).attr('checked')) {
|
131 |
+
$i.attr('disabled', false);
|
132 |
+
} else {
|
133 |
+
$i.attr('disabled', true)
|
134 |
+
.attr('checked', false);
|
135 |
+
}
|
136 |
+
})
|
137 |
+
.change();
|
138 |
+
|
139 |
+
// option filter_excl_sel
|
140 |
+
$('input#phpquery')
|
141 |
+
.change(function () {
|
142 |
+
if ($(this).attr('checked')) {
|
143 |
+
$('.filter_excl_sel').fadeIn();
|
144 |
+
} else {
|
145 |
+
$('.filter_excl_sel').fadeOut();
|
146 |
+
}
|
147 |
+
})
|
148 |
+
.change();
|
149 |
+
|
150 |
+
// refresh page when updated menu position
|
151 |
+
$('#menu_position').parents('form.ajax-form').on('ajax_saved_options', function () {
|
152 |
+
var s = $(this).val() || '';
|
153 |
+
window.location.href = s + (s.indexOf('?') > -1 ? '&' : '?') + 'page=wp_external_links&settings-updated=true';
|
154 |
+
});
|
155 |
+
|
156 |
+
// set tooltips
|
157 |
+
$('.tooltip-help').css('margin', '0 5px').tipsy({ fade: true, live: true, gravity: 'w', fallback: 'No help text.' });
|
158 |
+
|
159 |
+
// remove class to fix button background
|
160 |
+
$('*[type="submit"]').removeClass('submit');
|
161 |
+
|
162 |
+
// slide postbox
|
163 |
+
$('.postbox').find('.handlediv, .hndle').click(function () {
|
164 |
+
var $inside = $(this).parent().find('.inside');
|
165 |
+
|
166 |
+
if ($inside.css('display') === 'block') {
|
167 |
+
$inside.css({ display: 'none' });
|
168 |
+
} else {
|
169 |
+
$inside.css({ display: 'block' });
|
170 |
+
}
|
171 |
+
});
|
172 |
+
|
173 |
+
});
|
js/src/wp-external-links.js
CHANGED
@@ -1,69 +1,69 @@
|
|
1 |
-
/* WP External Links */
|
2 |
-
/*global jQuery, window*/
|
3 |
-
(function (undefined) {
|
4 |
-
'use strict';
|
5 |
-
|
6 |
-
var $ = jQuery === undefined ? null : jQuery;
|
7 |
-
|
8 |
-
// add event handler
|
9 |
-
function addEvt(el, evt, fn) {
|
10 |
-
if (el.attachEvent) {
|
11 |
-
// IE method
|
12 |
-
el.attachEvent('on' + evt, fn);
|
13 |
-
} else if (el.addEventListener) {
|
14 |
-
// Standard JS method
|
15 |
-
el.addEventListener(evt, fn, false);
|
16 |
-
}
|
17 |
-
}
|
18 |
-
|
19 |
-
// open external link
|
20 |
-
function openExtLink(a, evt) {
|
21 |
-
var target = a.getAttribute('data-wpel-target');
|
22 |
-
var href = a.getAttribute('href');
|
23 |
-
var win;
|
24 |
-
|
25 |
-
if (href && target) {
|
26 |
-
// open link in a new window
|
27 |
-
win = window.open(href, target);
|
28 |
-
win.focus();
|
29 |
-
|
30 |
-
// prevent default event action
|
31 |
-
if (evt) {
|
32 |
-
if (evt.preventDefault !== undefined) {
|
33 |
-
evt.preventDefault();
|
34 |
-
} else if (evt.returnValue !== undefined) {
|
35 |
-
evt.returnValue = false;
|
36 |
-
}
|
37 |
-
}
|
38 |
-
}
|
39 |
-
}
|
40 |
-
|
41 |
-
if ($) {
|
42 |
-
// jQuery DOMready method
|
43 |
-
$(function () {
|
44 |
-
$('a').live('click', function (evt) {
|
45 |
-
openExtLink(this, evt);
|
46 |
-
});
|
47 |
-
});
|
48 |
-
} else {
|
49 |
-
// use onload when jQuery not available
|
50 |
-
addEvt(window, 'load', function () {
|
51 |
-
var links = window.document.getElementsByTagName('a');
|
52 |
-
var eventClick = function (evt) {
|
53 |
-
var target = this instanceof Element ? this : evt.target;
|
54 |
-
openExtLink(target, evt);
|
55 |
-
};
|
56 |
-
var a;
|
57 |
-
var i;
|
58 |
-
|
59 |
-
// check each <a> element
|
60 |
-
for (i = 0; i < links.length; i += 1) {
|
61 |
-
a = links[i];
|
62 |
-
|
63 |
-
// click event for opening in a new window
|
64 |
-
addEvt(a, 'click', eventClick);
|
65 |
-
}
|
66 |
-
});
|
67 |
-
}
|
68 |
-
|
69 |
-
}());
|
1 |
+
/* WP External Links */
|
2 |
+
/*global jQuery, window*/
|
3 |
+
(function (undefined) {
|
4 |
+
'use strict';
|
5 |
+
|
6 |
+
var $ = jQuery === undefined ? null : jQuery;
|
7 |
+
|
8 |
+
// add event handler
|
9 |
+
function addEvt(el, evt, fn) {
|
10 |
+
if (el.attachEvent) {
|
11 |
+
// IE method
|
12 |
+
el.attachEvent('on' + evt, fn);
|
13 |
+
} else if (el.addEventListener) {
|
14 |
+
// Standard JS method
|
15 |
+
el.addEventListener(evt, fn, false);
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
// open external link
|
20 |
+
function openExtLink(a, evt) {
|
21 |
+
var target = a.getAttribute('data-wpel-target');
|
22 |
+
var href = a.getAttribute('href');
|
23 |
+
var win;
|
24 |
+
|
25 |
+
if (href && target) {
|
26 |
+
// open link in a new window
|
27 |
+
win = window.open(href, target);
|
28 |
+
win.focus();
|
29 |
+
|
30 |
+
// prevent default event action
|
31 |
+
if (evt) {
|
32 |
+
if (evt.preventDefault !== undefined) {
|
33 |
+
evt.preventDefault();
|
34 |
+
} else if (evt.returnValue !== undefined) {
|
35 |
+
evt.returnValue = false;
|
36 |
+
}
|
37 |
+
}
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
if ($) {
|
42 |
+
// jQuery DOMready method
|
43 |
+
$(function () {
|
44 |
+
$('a').live('click', function (evt) {
|
45 |
+
openExtLink(this, evt);
|
46 |
+
});
|
47 |
+
});
|
48 |
+
} else {
|
49 |
+
// use onload when jQuery not available
|
50 |
+
addEvt(window, 'load', function () {
|
51 |
+
var links = window.document.getElementsByTagName('a');
|
52 |
+
var eventClick = function (evt) {
|
53 |
+
var target = this instanceof Element ? this : evt.target;
|
54 |
+
openExtLink(target, evt);
|
55 |
+
};
|
56 |
+
var a;
|
57 |
+
var i;
|
58 |
+
|
59 |
+
// check each <a> element
|
60 |
+
for (i = 0; i < links.length; i += 1) {
|
61 |
+
a = links[i];
|
62 |
+
|
63 |
+
// click event for opening in a new window
|
64 |
+
addEvt(a, 'click', eventClick);
|
65 |
+
}
|
66 |
+
});
|
67 |
+
}
|
68 |
+
|
69 |
+
}());
|
js/src/wp-option-forms.js
CHANGED
@@ -1,55 +1,55 @@
|
|
1 |
-
/* WP Options Form */
|
2 |
-
/*global jQuery, ajaxurl*/
|
3 |
-
jQuery(function ($) {
|
4 |
-
'use strict';
|
5 |
-
|
6 |
-
// save function
|
7 |
-
var saveAjaxForm = function (target) {
|
8 |
-
var $this = $(target),
|
9 |
-
$form = $this.parents('form'),
|
10 |
-
// get ajax post values
|
11 |
-
vals = $form.serializeArray();
|
12 |
-
|
13 |
-
// disable button
|
14 |
-
$this.attr('disabled', true);
|
15 |
-
|
16 |
-
// show ajax loader
|
17 |
-
$form.find('.ajax-feedback').css('visibility', 'visible');
|
18 |
-
|
19 |
-
// save option values
|
20 |
-
$.post(ajaxurl, vals, function (result) {
|
21 |
-
var $msg = $('<strong>').insertBefore($this);
|
22 |
-
|
23 |
-
if (result === '1') {
|
24 |
-
$msg.html('Saved');
|
25 |
-
} else {
|
26 |
-
// save options, non-ajax fallback
|
27 |
-
$form.find('[name="action"]').val('update');
|
28 |
-
// normal submit
|
29 |
-
$form.submit();
|
30 |
-
}
|
31 |
-
|
32 |
-
$msg.css({ margin: '0 5px' })
|
33 |
-
.delay(1000)
|
34 |
-
.fadeOut(function () {
|
35 |
-
$(this).remove();
|
36 |
-
});
|
37 |
-
|
38 |
-
// enable button
|
39 |
-
$this.attr('disabled', false);
|
40 |
-
|
41 |
-
// hide ajax loader
|
42 |
-
$form.find('.ajax-feedback').css('visibility', 'hidden');
|
43 |
-
|
44 |
-
// trigger ajax_saved_options
|
45 |
-
$form.trigger('ajax_saved_options', [result]);
|
46 |
-
});
|
47 |
-
};
|
48 |
-
|
49 |
-
// add ajax post
|
50 |
-
$('form.ajax-form input[type="submit"]').click(function (e) {
|
51 |
-
saveAjaxForm(this);
|
52 |
-
e.preventDefault();
|
53 |
-
});
|
54 |
-
|
55 |
-
});
|
1 |
+
/* WP Options Form */
|
2 |
+
/*global jQuery, ajaxurl*/
|
3 |
+
jQuery(function ($) {
|
4 |
+
'use strict';
|
5 |
+
|
6 |
+
// save function
|
7 |
+
var saveAjaxForm = function (target) {
|
8 |
+
var $this = $(target),
|
9 |
+
$form = $this.parents('form'),
|
10 |
+
// get ajax post values
|
11 |
+
vals = $form.serializeArray();
|
12 |
+
|
13 |
+
// disable button
|
14 |
+
$this.attr('disabled', true);
|
15 |
+
|
16 |
+
// show ajax loader
|
17 |
+
$form.find('.ajax-feedback').css('visibility', 'visible');
|
18 |
+
|
19 |
+
// save option values
|
20 |
+
$.post(ajaxurl, vals, function (result) {
|
21 |
+
var $msg = $('<strong>').insertBefore($this);
|
22 |
+
|
23 |
+
if (result === '1') {
|
24 |
+
$msg.html('Saved');
|
25 |
+
} else {
|
26 |
+
// save options, non-ajax fallback
|
27 |
+
$form.find('[name="action"]').val('update');
|
28 |
+
// normal submit
|
29 |
+
$form.submit();
|
30 |
+
}
|
31 |
+
|
32 |
+
$msg.css({ margin: '0 5px' })
|
33 |
+
.delay(1000)
|
34 |
+
.fadeOut(function () {
|
35 |
+
$(this).remove();
|
36 |
+
});
|
37 |
+
|
38 |
+
// enable button
|
39 |
+
$this.attr('disabled', false);
|
40 |
+
|
41 |
+
// hide ajax loader
|
42 |
+
$form.find('.ajax-feedback').css('visibility', 'hidden');
|
43 |
+
|
44 |
+
// trigger ajax_saved_options
|
45 |
+
$form.trigger('ajax_saved_options', [result]);
|
46 |
+
});
|
47 |
+
};
|
48 |
+
|
49 |
+
// add ajax post
|
50 |
+
$('form.ajax-form input[type="submit"]').click(function (e) {
|
51 |
+
saveAjaxForm(this);
|
52 |
+
e.preventDefault();
|
53 |
+
});
|
54 |
+
|
55 |
+
});
|
js/wp-external-links.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1 |
-
/* WP External Links */
|
2 |
-
(function(e){"use strict";function n(e,t,n){if(e.attachEvent){e.attachEvent("on"+t,n)}else if(e.addEventListener){e.addEventListener(t,n,false)}}function r(t,n){var r=t.getAttribute("data-wpel-target");var i=t.getAttribute("href");var s;if(i&&r){s=window.open(i,r);s.focus();if(n){if(n.preventDefault!==e){n.preventDefault()}else if(n.returnValue!==e){n.returnValue=false}}}}var t=jQuery===e?null:jQuery;if(t){t(function(){t("body").on("click","a",function(e){r(this,e)})})}else{n(window,"load",function(){var e=window.document.getElementsByTagName("a");var t=function(e){var t=this instanceof Element?this:e.target;r(t,e)};var i;var s;for(s=0;s<e.length;s+=1){i=e[s];n(i,"click",t)}})}})()
|
1 |
+
/* WP External Links */
|
2 |
+
(function(e){"use strict";function n(e,t,n){if(e.attachEvent){e.attachEvent("on"+t,n)}else if(e.addEventListener){e.addEventListener(t,n,false)}}function r(t,n){var r=t.getAttribute("data-wpel-target");var i=t.getAttribute("href");var s;if(i&&r){s=window.open(i,r);s.focus();if(n){if(n.preventDefault!==e){n.preventDefault()}else if(n.returnValue!==e){n.returnValue=false}}}}var t=jQuery===e?null:jQuery;if(t){t(function(){t("body").on("click","a",function(e){r(this,e)})})}else{n(window,"load",function(){var e=window.document.getElementsByTagName("a");var t=function(e){var t=this instanceof Element?this:e.target;r(t,e)};var i;var s;for(s=0;s<e.length;s+=1){i=e[s];n(i,"click",t)}})}})()
|
js/wp-option-forms.js
DELETED
@@ -1,55 +0,0 @@
|
|
1 |
-
/* WP Options Form */
|
2 |
-
/*global jQuery, ajaxurl*/
|
3 |
-
jQuery(function ($) {
|
4 |
-
'use strict';
|
5 |
-
|
6 |
-
// save function
|
7 |
-
var saveAjaxForm = function (target) {
|
8 |
-
var $this = $(target),
|
9 |
-
$form = $this.parents('form'),
|
10 |
-
// get ajax post values
|
11 |
-
vals = $form.serializeArray();
|
12 |
-
|
13 |
-
// disable button
|
14 |
-
$this.attr('disabled', true);
|
15 |
-
|
16 |
-
// show ajax loader
|
17 |
-
$form.find('.ajax-feedback').css('visibility', 'visible');
|
18 |
-
|
19 |
-
// save option values
|
20 |
-
$.post(ajaxurl, vals, function (result) {
|
21 |
-
var $msg = $('<strong>').insertBefore($this);
|
22 |
-
|
23 |
-
if (result === '1') {
|
24 |
-
$msg.html('Saved');
|
25 |
-
} else {
|
26 |
-
// save options, non-ajax fallback
|
27 |
-
$form.find('[name="action"]').val('update');
|
28 |
-
// normal submit
|
29 |
-
$form.submit();
|
30 |
-
}
|
31 |
-
|
32 |
-
$msg.css({ margin: '0 5px' })
|
33 |
-
.delay(1000)
|
34 |
-
.fadeOut(function(){
|
35 |
-
$(this).remove();
|
36 |
-
});
|
37 |
-
|
38 |
-
// enable button
|
39 |
-
$this.attr('disabled', false);
|
40 |
-
|
41 |
-
// hide ajax loader
|
42 |
-
$form.find('.ajax-feedback').css('visibility', 'hidden');
|
43 |
-
|
44 |
-
// trigger ajax_saved_options
|
45 |
-
$form.trigger('ajax_saved_options', [result]);
|
46 |
-
});
|
47 |
-
};
|
48 |
-
|
49 |
-
// add ajax post
|
50 |
-
$('form.ajax-form input[type="submit"]').click(function(e) {
|
51 |
-
saveAjaxForm(this);
|
52 |
-
e.preventDefault();
|
53 |
-
});
|
54 |
-
|
55 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readme.txt
CHANGED
@@ -1,240 +1,245 @@
|
|
1 |
-
=== WP External Links (nofollow new window seo) ===
|
2 |
-
Contributors: freelancephp
|
3 |
-
Tags: links, external, icon, target, _blank, _new, _none, rel, nofollow, new window, new tab, javascript, xhtml, seo
|
4 |
-
Requires at least: 3.4.0
|
5 |
-
Tested up to: 4.
|
6 |
-
Stable tag: 1.
|
7 |
-
|
8 |
-
Open external links in a new window or tab, adding "nofollow", set link icon, styling, SEO friendly options and more. Easy install and go.
|
9 |
-
|
10 |
-
== Description ==
|
11 |
-
|
12 |
-
Configure settings for all external links on your site.
|
13 |
-
|
14 |
-
= Features =
|
15 |
-
* Open in new window or tab
|
16 |
-
* Add "nofollow"
|
17 |
-
* Choose from 20 icons
|
18 |
-
* Set other link options (like classes, title etc)
|
19 |
-
* Make it SEO friendly
|
20 |
-
|
21 |
-
= Easy to use =
|
22 |
-
After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
|
23 |
-
|
24 |
-
= On the fly =
|
25 |
-
The plugin will change the output of the (external) links on the fly. So when you deactivate the plugin, all contents will remain the same as it was before installing the plugin.
|
26 |
-
|
27 |
-
= Sources =
|
28 |
-
* [Documentation](http://wordpress.org/extend/plugins/wp-external-links/other_notes/)
|
29 |
-
* [FAQ](http://wordpress.org/extend/plugins/wp-external-links/faq/)
|
30 |
-
* [Github](https://github.com/freelancephp/WP-External-Links)
|
31 |
-
|
32 |
-
= Like this plugin? =
|
33 |
-
[Send your review](http://wordpress.org/support/view/plugin-reviews/wp-external-links-plugin).
|
34 |
-
|
35 |
-
|
36 |
-
== Installation ==
|
37 |
-
|
38 |
-
1. Go to `Plugins` in the Admin menu
|
39 |
-
1. Click on the button `Add new`
|
40 |
-
1. Search for `WP External Links` and click 'Install Now' OR click on the `upload` link to upload `wp-external-links.zip`
|
41 |
-
1. Click on `Activate plugin`
|
42 |
-
|
43 |
-
== Frequently Asked Questions ==
|
44 |
-
|
45 |
-
= I want internal links to be treated as external links. How? =
|
46 |
-
|
47 |
-
You could add `rel="external"` to those internal links that should be treated as external. The plugin settings will also be applied to those links.
|
48 |
-
|
49 |
-
= Links to my own domain are treated as external links. Why? =
|
50 |
-
|
51 |
-
Links pointing to your WordPress site (`wp_url`) are internal links. All other links will be treated as external links.
|
52 |
-
|
53 |
-
= All links to my subdomains should be treated internal links. How? =
|
54 |
-
|
55 |
-
Add your main domain to the option "Ingore links (URL) containing..." and they will not be treated as external.
|
56 |
-
|
57 |
-
[Do you have a question? Please ask me](http://www.freelancephp.net/contact/)
|
58 |
-
|
59 |
-
== Screenshots ==
|
60 |
-
|
61 |
-
1. Link Icon on the Site
|
62 |
-
1. Admin Settings Page
|
63 |
-
|
64 |
-
== Documentation ==
|
65 |
-
|
66 |
-
After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
|
67 |
-
|
68 |
-
= Action hook: wpel_ready =
|
69 |
-
The plugin also has a hook when ready, f.e. to add extra filters:
|
70 |
-
`function extra_filters($filter_callback, $object) {
|
71 |
-
add_filter('some_filter', $filter_callback);
|
72 |
-
}
|
73 |
-
add_action('wpel_ready', 'extra_filters');`
|
74 |
-
|
75 |
-
= Filter hook 1: wpel_external_link =
|
76 |
-
The wpel_external_link filter gives you the possibility to manipulate output of the mailto created by the plugin, like:
|
77 |
-
`function special_external_link($created_link, $original_link, $label, $attrs, $is_ignored_link) {
|
78 |
-
// skip links that contain the class "not-external"
|
79 |
-
if (isset($attrs['class']) && strpos($attrs['class'], 'not-external') !== false) {
|
80 |
-
return $original_link;
|
81 |
-
}
|
82 |
-
|
83 |
-
return '<b>'. $created_link .'</b>';
|
84 |
-
}
|
85 |
-
|
86 |
-
add_filter('wpel_external_link', 'special_external_link', 10, 5);`
|
87 |
-
|
88 |
-
Now all external links will be processed and wrapped around a `<b>`-tag. And links containing the class "not-external" will not be processed by the plugin at all (and stay the way they are).
|
89 |
-
|
90 |
-
|
91 |
-
= Filter hook 2: wpel_internal_link =
|
92 |
-
With the internal filter you can manipulate the output of the internal links on your site. F.e.:
|
93 |
-
`
|
94 |
-
function special_internal_link($link, $label, $attrs) {
|
95 |
-
return '<b>'. $link .'</b>';
|
96 |
-
}
|
97 |
-
|
98 |
-
add_filter('wpel_internal_link', 'special_internal_link', 10, 3);`
|
99 |
-
|
100 |
-
In this case all internal links will be made bold.
|
101 |
-
|
102 |
-
= Credits =
|
103 |
-
* [jQuery Tipsy Plugin](http://plugins.jquery.com/project/tipsy) made by [Jason Frame](http://onehackoranother.com/)
|
104 |
-
* [phpQuery](http://code.google.com/p/phpquery/) made by [Tobiasz Cudnik](http://tobiasz123.wordpress.com)
|
105 |
-
* [Icon](http://findicons.com/icon/164579/link_go?id=427009) made by [FatCow Web Hosting](http://www.fatcow.com/)
|
106 |
-
|
107 |
-
== Changelog ==
|
108 |
-
|
109 |
-
= 1.
|
110 |
-
* Fixed bug
|
111 |
-
* Fixed bug
|
112 |
-
*
|
113 |
-
|
114 |
-
= 1.
|
115 |
-
* Fixed bug
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
* Fixed
|
124 |
-
*
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
* Fixed
|
129 |
-
* Fixed
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
*
|
134 |
-
* Fixed
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
* Fixed
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
*
|
145 |
-
|
146 |
-
|
147 |
-
*
|
148 |
-
|
149 |
-
|
150 |
-
* Fixed
|
151 |
-
* Fixed
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
*
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
*
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
*
|
164 |
-
*
|
165 |
-
|
166 |
-
|
167 |
-
*
|
168 |
-
|
169 |
-
|
170 |
-
*
|
171 |
-
*
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
*
|
186 |
-
|
187 |
-
|
188 |
-
*
|
189 |
-
*
|
190 |
-
*
|
191 |
-
*
|
192 |
-
*
|
193 |
-
|
194 |
-
|
195 |
-
*
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
*
|
201 |
-
|
202 |
-
= 0.
|
203 |
-
* Added option to
|
204 |
-
*
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
*
|
209 |
-
*
|
210 |
-
|
211 |
-
= 0.
|
212 |
-
*
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
*
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
*
|
230 |
-
*
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
*
|
235 |
-
*
|
236 |
-
|
237 |
-
= 0.
|
238 |
-
*
|
239 |
-
*
|
240 |
-
*
|
|
|
|
|
|
|
|
|
|
1 |
+
=== WP External Links (nofollow new window seo) ===
|
2 |
+
Contributors: freelancephp
|
3 |
+
Tags: links, external, icon, target, _blank, _new, _none, rel, nofollow, new window, new tab, javascript, xhtml, seo
|
4 |
+
Requires at least: 3.4.0
|
5 |
+
Tested up to: 4.1.0
|
6 |
+
Stable tag: 1.56
|
7 |
+
|
8 |
+
Open external links in a new window or tab, adding "nofollow", set link icon, styling, SEO friendly options and more. Easy install and go.
|
9 |
+
|
10 |
+
== Description ==
|
11 |
+
|
12 |
+
Configure settings for all external links on your site.
|
13 |
+
|
14 |
+
= Features =
|
15 |
+
* Open in new window or tab
|
16 |
+
* Add "nofollow"
|
17 |
+
* Choose from 20 icons
|
18 |
+
* Set other link options (like classes, title etc)
|
19 |
+
* Make it SEO friendly
|
20 |
+
|
21 |
+
= Easy to use =
|
22 |
+
After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
|
23 |
+
|
24 |
+
= On the fly =
|
25 |
+
The plugin will change the output of the (external) links on the fly. So when you deactivate the plugin, all contents will remain the same as it was before installing the plugin.
|
26 |
+
|
27 |
+
= Sources =
|
28 |
+
* [Documentation](http://wordpress.org/extend/plugins/wp-external-links/other_notes/)
|
29 |
+
* [FAQ](http://wordpress.org/extend/plugins/wp-external-links/faq/)
|
30 |
+
* [Github](https://github.com/freelancephp/WP-External-Links)
|
31 |
+
|
32 |
+
= Like this plugin? =
|
33 |
+
[Send your review](http://wordpress.org/support/view/plugin-reviews/wp-external-links-plugin).
|
34 |
+
|
35 |
+
|
36 |
+
== Installation ==
|
37 |
+
|
38 |
+
1. Go to `Plugins` in the Admin menu
|
39 |
+
1. Click on the button `Add new`
|
40 |
+
1. Search for `WP External Links` and click 'Install Now' OR click on the `upload` link to upload `wp-external-links.zip`
|
41 |
+
1. Click on `Activate plugin`
|
42 |
+
|
43 |
+
== Frequently Asked Questions ==
|
44 |
+
|
45 |
+
= I want internal links to be treated as external links. How? =
|
46 |
+
|
47 |
+
You could add `rel="external"` to those internal links that should be treated as external. The plugin settings will also be applied to those links.
|
48 |
+
|
49 |
+
= Links to my own domain are treated as external links. Why? =
|
50 |
+
|
51 |
+
Links pointing to your WordPress site (`wp_url`) are internal links. All other links will be treated as external links.
|
52 |
+
|
53 |
+
= All links to my subdomains should be treated internal links. How? =
|
54 |
+
|
55 |
+
Add your main domain to the option "Ingore links (URL) containing..." and they will not be treated as external.
|
56 |
+
|
57 |
+
[Do you have a question? Please ask me](http://www.freelancephp.net/contact/)
|
58 |
+
|
59 |
+
== Screenshots ==
|
60 |
+
|
61 |
+
1. Link Icon on the Site
|
62 |
+
1. Admin Settings Page
|
63 |
+
|
64 |
+
== Documentation ==
|
65 |
+
|
66 |
+
After activating the plugin all options are already set to make your external links SEO friendly. Optionally you can also set the target for opening in a new window or tab or styling options, like adding an icon.
|
67 |
+
|
68 |
+
= Action hook: wpel_ready =
|
69 |
+
The plugin also has a hook when ready, f.e. to add extra filters:
|
70 |
+
`function extra_filters($filter_callback, $object) {
|
71 |
+
add_filter('some_filter', $filter_callback);
|
72 |
+
}
|
73 |
+
add_action('wpel_ready', 'extra_filters');`
|
74 |
+
|
75 |
+
= Filter hook 1: wpel_external_link =
|
76 |
+
The wpel_external_link filter gives you the possibility to manipulate output of the mailto created by the plugin, like:
|
77 |
+
`function special_external_link($created_link, $original_link, $label, $attrs, $is_ignored_link) {
|
78 |
+
// skip links that contain the class "not-external"
|
79 |
+
if (isset($attrs['class']) && strpos($attrs['class'], 'not-external') !== false) {
|
80 |
+
return $original_link;
|
81 |
+
}
|
82 |
+
|
83 |
+
return '<b>'. $created_link .'</b>';
|
84 |
+
}
|
85 |
+
|
86 |
+
add_filter('wpel_external_link', 'special_external_link', 10, 5);`
|
87 |
+
|
88 |
+
Now all external links will be processed and wrapped around a `<b>`-tag. And links containing the class "not-external" will not be processed by the plugin at all (and stay the way they are).
|
89 |
+
|
90 |
+
|
91 |
+
= Filter hook 2: wpel_internal_link =
|
92 |
+
With the internal filter you can manipulate the output of the internal links on your site. F.e.:
|
93 |
+
`
|
94 |
+
function special_internal_link($link, $label, $attrs) {
|
95 |
+
return '<b>'. $link .'</b>';
|
96 |
+
}
|
97 |
+
|
98 |
+
add_filter('wpel_internal_link', 'special_internal_link', 10, 3);`
|
99 |
+
|
100 |
+
In this case all internal links will be made bold.
|
101 |
+
|
102 |
+
= Credits =
|
103 |
+
* [jQuery Tipsy Plugin](http://plugins.jquery.com/project/tipsy) made by [Jason Frame](http://onehackoranother.com/)
|
104 |
+
* [phpQuery](http://code.google.com/p/phpquery/) made by [Tobiasz Cudnik](http://tobiasz123.wordpress.com)
|
105 |
+
* [Icon](http://findicons.com/icon/164579/link_go?id=427009) made by [FatCow Web Hosting](http://www.fatcow.com/)
|
106 |
+
|
107 |
+
== Changelog ==
|
108 |
+
|
109 |
+
= 1.56 =
|
110 |
+
* Fixed bug jQuery as dependency for js scripts
|
111 |
+
* Fixed bug "no-icon class in same window" working with javascript
|
112 |
+
* Fixed bug setting defaults on installation
|
113 |
+
|
114 |
+
= 1.55 =
|
115 |
+
* Fixed bug JS error: Uncaught TypeError: undefined is not a function
|
116 |
+
* Fixed bug PHP error for links without href attribute ("undefined index href")
|
117 |
+
* Replaced deprecated jQuery .live() to .on() (contribution by Alonbilu)
|
118 |
+
|
119 |
+
= 1.54 =
|
120 |
+
* Fixed bug opening links containing html tags (like <b>)
|
121 |
+
|
122 |
+
= 1.53 =
|
123 |
+
* Fixed bug also opening ignored URL's on other tab/window when using javascript
|
124 |
+
* Changed javascript open method (data-attribute)
|
125 |
+
|
126 |
+
= 1.52 =
|
127 |
+
* Added filter hook wpel_internal_link
|
128 |
+
* Fixed use_js option bug
|
129 |
+
* Fixed bug loading non-existing stylesheet
|
130 |
+
* Minified javascripts
|
131 |
+
|
132 |
+
= 1.51 =
|
133 |
+
* Fixed also check url's starting with //
|
134 |
+
* Fixed wpel_external_link also applied on ignored links
|
135 |
+
|
136 |
+
= 1.50 =
|
137 |
+
* Removed stylesheet file to save extra request
|
138 |
+
* Added option for loading js file in wp_footer
|
139 |
+
* Fixed bug with data-* attributes
|
140 |
+
* Fixed bug url's with hash at the end
|
141 |
+
* Fixed PHP errors
|
142 |
+
|
143 |
+
= 1.41 =
|
144 |
+
* Fixed Bug: wpmel_external_link filter hook was not working correctly
|
145 |
+
|
146 |
+
= 1.40 =
|
147 |
+
* Added action hook wpel_ready
|
148 |
+
* Added filter hook wpel_external_link
|
149 |
+
* Added output flush on wp_footer
|
150 |
+
* Fixed Bug: spaces before url in href-attribute not recognized as external link
|
151 |
+
* Fixed Bug: external links not processed (regexpr tag conflict starting with an a, like <aside> or <article>)
|
152 |
+
* Cosmetic changes: added "Admin Settings", replaced help icon, restyled tooltip texts, removed "About this plugin" box
|
153 |
+
|
154 |
+
= 1.31 =
|
155 |
+
* Fixed passing arguments by reference using & (deprecated for PHP 5.4+)
|
156 |
+
* Fixed options save failure by adding a non-ajax submit fallback
|
157 |
+
|
158 |
+
= 1.30 =
|
159 |
+
* Re-arranged options in metaboxes
|
160 |
+
* Added option for no icons on images
|
161 |
+
|
162 |
+
= 1.21 =
|
163 |
+
* Fixed phpQuery bugs (class already exists and loading stylesheet)
|
164 |
+
* Solved php notices
|
165 |
+
|
166 |
+
= 1.20 =
|
167 |
+
* Added option to ignore certain links or domains
|
168 |
+
* Solved tweet button problem by adding link to new ignore option
|
169 |
+
* Made JavaScript method consistent to not using JS
|
170 |
+
* Solved PHP warnings
|
171 |
+
* Solved bug adding own class
|
172 |
+
* Changed bloginfo "url" to "wpurl"
|
173 |
+
|
174 |
+
= 1.10 =
|
175 |
+
* Resolved old parsing method (same as version 0.35)
|
176 |
+
* Option to use phpQuery for parsing (for those who didn't experience problems with version 1.03)
|
177 |
+
|
178 |
+
= 1.03 =
|
179 |
+
* Workaround for echo DOCTYPE bug (caused by attributes in the head-tag)
|
180 |
+
|
181 |
+
= 1.02 =
|
182 |
+
* Solved the not working activation hook
|
183 |
+
|
184 |
+
= 1.01 =
|
185 |
+
* Solved bug after live testing
|
186 |
+
|
187 |
+
= 1.00 =
|
188 |
+
* Added option for setting title-attribute
|
189 |
+
* Added option for excluding filtering certain external links
|
190 |
+
* Added Admin help tooltips using jQuery Tipsy Plugin
|
191 |
+
* Reorginized files and refactored code to PHP5 (no support for PHP4)
|
192 |
+
* Added WP built-in meta box functionallity (using the `WP_Meta_Box_Page` Class)
|
193 |
+
* Reorganized saving options and added Ajax save method (using the `WP_Option_Forms` Class)
|
194 |
+
* Removed Regexp and using phpQuery
|
195 |
+
* Choose menu position for this plugin (see "Screen Options")
|
196 |
+
* Removed possibility to convert all `<a>` tags to xhtml clean code (so only external links will be converted)
|
197 |
+
* Removed "Solve problem" options
|
198 |
+
|
199 |
+
= 0.35 =
|
200 |
+
* Widget Logic options bug
|
201 |
+
|
202 |
+
= 0.34 =
|
203 |
+
* Added option only converting external `<a>` tags to XHTML valid code
|
204 |
+
* Changed script attribute `language` to `type`
|
205 |
+
* Added support for widget_content filter of the Logic Widget plugin
|
206 |
+
|
207 |
+
= 0.33 =
|
208 |
+
* Added option to fix js problem
|
209 |
+
* Fixed PHP / WP notices
|
210 |
+
|
211 |
+
= 0.32 =
|
212 |
+
* For jQuery uses live() function so also opens dynamicly created links in given target
|
213 |
+
* Fixed bug of changing `<abbr>` tag
|
214 |
+
* Small cosmetical adjustments
|
215 |
+
|
216 |
+
= 0.31 =
|
217 |
+
* Small cosmetical adjustments
|
218 |
+
|
219 |
+
= 0.30 =
|
220 |
+
* Improved Admin Options, f.e. target option looks more like the Blogroll target option
|
221 |
+
* Added option for choosing which content should be filtered
|
222 |
+
|
223 |
+
= 0.21 =
|
224 |
+
* Solved bug removing icon stylesheet
|
225 |
+
|
226 |
+
= 0.20 =
|
227 |
+
* Put icon styles in external stylesheet
|
228 |
+
* Can use "ext-icon-..." to show a specific icon on a link
|
229 |
+
* Added option to set your own No-Icon class
|
230 |
+
* Made "Class" optional, so it's not used for showing icons anymore
|
231 |
+
* Added 3 more icons
|
232 |
+
|
233 |
+
= 0.12 =
|
234 |
+
* Options are organized more logical
|
235 |
+
* Added some more icons
|
236 |
+
|
237 |
+
= 0.11 =
|
238 |
+
* JavaScript uses window.open() (tested in FireFox Opera, Safari, Chrome and IE6+)
|
239 |
+
* Also possible to open all external links in the same new window
|
240 |
+
* Some layout changes on the Admin Options Page
|
241 |
+
|
242 |
+
= 0.10 =
|
243 |
+
* Features: opening in a new window, set link icon, set "external", set "nofollow", set css-class
|
244 |
+
* Replaces external links by clean XHTML <a> tags
|
245 |
+
* Internalization implemented (no language files yet)
|
wp-external-links.php
CHANGED
@@ -1,53 +1,58 @@
|
|
1 |
-
<?php defined('ABSPATH') OR die('No direct access.');
|
2 |
-
/*
|
3 |
-
Plugin Name: WP External Links
|
4 |
-
Plugin URI: http://www.freelancephp.net/wp-external-links-plugin
|
5 |
-
Description: Open external links in a new window/tab, add "external" / "nofollow" to rel-attribute, set icon, XHTML strict, SEO friendly...
|
6 |
-
Author: Victor Villaverde Laan
|
7 |
-
Version: 1.
|
8 |
-
Author URI: http://www.freelancephp.net
|
9 |
-
License: Dual licensed under the MIT and GPL licenses
|
10 |
-
*/
|
11 |
-
|
12 |
-
// constants
|
13 |
-
if (!defined('WP_EXTERNAL_LINKS_FILE')) { define('WP_EXTERNAL_LINKS_FILE', __FILE__); }
|
14 |
-
if (!defined('WP_EXTERNAL_LINKS_VERSION')) { define('WP_EXTERNAL_LINKS_VERSION', '1.
|
15 |
-
if (!defined('WP_EXTERNAL_LINKS_KEY')) { define('WP_EXTERNAL_LINKS_KEY', 'wp_external_links'); }
|
16 |
-
if (!defined('WP_EXTERNAL_LINKS_DOMAIN')) { define('WP_EXTERNAL_LINKS_DOMAIN', 'wp-external-links'); }
|
17 |
-
if (!defined('WP_EXTERNAL_LINKS_OPTIONS_NAME')) { define('WP_EXTERNAL_LINKS_OPTIONS_NAME', 'WP_External_Links_options'); }
|
18 |
-
if (!defined('WP_EXTERNAL_LINKS_ADMIN_PAGE')) { define('WP_EXTERNAL_LINKS_ADMIN_PAGE', 'wp-external-links-settings'); }
|
19 |
-
|
20 |
-
// check plugin compatibility
|
21 |
-
if (isset($wp_version)
|
22 |
-
AND version_compare(preg_replace('/-.*$/', '', $wp_version), '3.4', '>=')
|
23 |
-
AND version_compare(phpversion(), '5.2.4', '>=')) {
|
24 |
-
|
25 |
-
// include classes
|
26 |
-
require_once('includes/wp-plugin-dev-classes/class-wp-meta-box-page.php');
|
27 |
-
require_once('includes/wp-plugin-dev-classes/class-wp-option-forms.php');
|
28 |
-
require_once('includes/class-admin-external-links.php');
|
29 |
-
require_once('includes/class-wp-external-links.php');
|
30 |
-
|
31 |
-
// create instance
|
32 |
-
$WP_External_Links = new WP_External_Links();
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
}
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php defined('ABSPATH') OR die('No direct access.');
|
2 |
+
/*
|
3 |
+
Plugin Name: WP External Links
|
4 |
+
Plugin URI: http://www.freelancephp.net/wp-external-links-plugin
|
5 |
+
Description: Open external links in a new window/tab, add "external" / "nofollow" to rel-attribute, set icon, XHTML strict, SEO friendly...
|
6 |
+
Author: Victor Villaverde Laan
|
7 |
+
Version: 1.56
|
8 |
+
Author URI: http://www.freelancephp.net
|
9 |
+
License: Dual licensed under the MIT and GPL licenses
|
10 |
+
*/
|
11 |
+
|
12 |
+
// constants
|
13 |
+
if (!defined('WP_EXTERNAL_LINKS_FILE')) { define('WP_EXTERNAL_LINKS_FILE', __FILE__); }
|
14 |
+
if (!defined('WP_EXTERNAL_LINKS_VERSION')) { define('WP_EXTERNAL_LINKS_VERSION', '1.56'); }
|
15 |
+
if (!defined('WP_EXTERNAL_LINKS_KEY')) { define('WP_EXTERNAL_LINKS_KEY', 'wp_external_links'); }
|
16 |
+
if (!defined('WP_EXTERNAL_LINKS_DOMAIN')) { define('WP_EXTERNAL_LINKS_DOMAIN', 'wp-external-links'); }
|
17 |
+
if (!defined('WP_EXTERNAL_LINKS_OPTIONS_NAME')) { define('WP_EXTERNAL_LINKS_OPTIONS_NAME', 'WP_External_Links_options'); }
|
18 |
+
if (!defined('WP_EXTERNAL_LINKS_ADMIN_PAGE')) { define('WP_EXTERNAL_LINKS_ADMIN_PAGE', 'wp-external-links-settings'); }
|
19 |
+
|
20 |
+
// check plugin compatibility
|
21 |
+
if (isset($wp_version)
|
22 |
+
AND version_compare(preg_replace('/-.*$/', '', $wp_version), '3.4', '>=')
|
23 |
+
AND version_compare(phpversion(), '5.2.4', '>=')) {
|
24 |
+
|
25 |
+
// include classes
|
26 |
+
require_once('includes/wp-plugin-dev-classes/class-wp-meta-box-page.php');
|
27 |
+
require_once('includes/wp-plugin-dev-classes/class-wp-option-forms.php');
|
28 |
+
require_once('includes/class-admin-external-links.php');
|
29 |
+
require_once('includes/class-wp-external-links.php');
|
30 |
+
|
31 |
+
// create instance
|
32 |
+
$WP_External_Links = new WP_External_Links();
|
33 |
+
|
34 |
+
// init test
|
35 |
+
if (class_exists('Test_WP_External_Links')) {
|
36 |
+
$Test_WP_External_Links = new Test_WP_External_Links;
|
37 |
+
}
|
38 |
+
|
39 |
+
} else {
|
40 |
+
|
41 |
+
// set error message
|
42 |
+
if (!function_exists('wpel_error_notice')):
|
43 |
+
function wpel_error_notice() {
|
44 |
+
$plugin_title = get_admin_page_title();
|
45 |
+
|
46 |
+
echo '<div class="error">'
|
47 |
+
. sprintf(__('<p>Warning - The plugin <strong>%s</strong> requires PHP 5.2.4+ and WP 3.4+. Please upgrade your PHP and/or WordPress.'
|
48 |
+
. '<br/>Disable the plugin to remove this message.</p>'
|
49 |
+
, WP_EXTERNAL_LINKS_KEY), $plugin_title)
|
50 |
+
. '</div>';
|
51 |
+
}
|
52 |
+
|
53 |
+
add_action('admin_notices', 'wpel_error_notice');
|
54 |
+
endif;
|
55 |
+
|
56 |
+
}
|
57 |
+
|
58 |
+
/* ommit PHP closing tag, to prevent unwanted whitespace at the end of the parts generated by the included files */
|