BJ Lazy Load - Version 0.5.0

Version Description

Lazy load images and iframes. Complete rewrite.

Download this release

Release Info

Developer bjornjohansen
Plugin Icon 128x128 BJ Lazy Load
Version 0.5.0
Comparing to
See all releases

Code changes from version 0.4.0 to 0.5.0

admin.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class BJLL_Admin_Page extends scbAdminPage {
4
+
5
+ function setup() {
6
+ $this->args = array(
7
+ 'menu_title' => 'BJ Lazy Load',
8
+ 'page_title' => __( 'BJ Lazy Load Options', 'bj_lazy_load' ),
9
+ );
10
+ }
11
+
12
+ function page_content() {
13
+
14
+
15
+ echo $this->form_table( array(
16
+ array(
17
+ 'title' => __( 'Lazy load images', 'bj_lazy_load' ),
18
+ 'type' => 'radio',
19
+ 'name' => 'lazy_load_images',
20
+ 'value' => array( 'yes' => __('Yes', 'bj_lazy_load'), 'no' => __('No', 'bj_lazy_load') ),
21
+ ),
22
+ array(
23
+ 'title' => __( 'Lazy load iframes', 'bj_lazy_load' ),
24
+ 'type' => 'radio',
25
+ 'name' => 'lazy_load_iframes',
26
+ 'value' => array( 'yes' => __('Yes', 'bj_lazy_load'), 'no' => __('No', 'bj_lazy_load') ),
27
+ ),
28
+ array(
29
+ 'title' => __( 'Theme loader function', 'bj_lazy_load' ),
30
+ 'type' => 'select',
31
+ 'name' => 'theme_loader_function',
32
+ 'value' => array( 'wp_footer', 'wp_head' ),
33
+
34
+ )
35
+ ) );
36
+
37
+ }
38
+
39
+ }
bj-lazy-load.php CHANGED
@@ -1,498 +1,170 @@
1
- <?php
2
- /*
3
- Plugin Name: BJ Lazy Load
4
- Plugin URI: http://wordpress.org/extend/plugins/bj-lazy-load/
5
- Description: Lazy image loading makes your site load faster and saves bandwidth.
6
- Version: 0.4.0
7
- Author: Bjørn Johansen
8
- Author URI: http://twitter.com/bjornjohansen
9
- License: GPL2
10
-
11
- Copyright 2011–2012 Bjørn Johansen (email : post@bjornjohansen.no)
12
-
13
- This program is free software; you can redistribute it and/or modify
14
- it under the terms of the GNU General Public License, version 2, as
15
- published by the Free Software Foundation.
16
-
17
- This program is distributed in the hope that it will be useful,
18
- but WITHOUT ANY WARRANTY; without even the implied warranty of
19
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
- GNU General Public License for more details.
21
-
22
- You should have received a copy of the GNU General Public License
23
- along with this program; if not, write to the Free Software
24
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
-
26
- */
27
-
28
-
29
- class BJLL {
30
-
31
- const version = '0.4.0';
32
- private $_placeholder_url;
33
-
34
- private static $_instance;
35
-
36
- function __construct() {
37
-
38
- $this->_placeholder_url = plugins_url( '/img/placeholder.gif', __FILE__ );
39
-
40
- if (get_option( 'bjll_include_css', 1 )) {
41
- add_action( 'wp_print_styles', array($this, 'enqueue_styles' ) );
42
- }
43
-
44
- if (get_option( 'bjll_include_js', 1 )) {
45
- add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
46
- }
47
-
48
- $theme_caller = get_option( 'bjll_theme_caller' );
49
- if ( $theme_caller == 'wp_head' ) {
50
- add_action( 'wp_print_scripts', array( $this, 'output_js_options' ) );
51
- } else {
52
- add_action( 'wp_print_footer_scripts', array( $this, 'output_js_options' ) );
53
- }
54
-
55
- add_action( 'wp_ajax_BJLL_get_images', array( $this, 'get_images_json' ) );
56
- add_action( 'wp_ajax_nopriv_BJLL_get_images', array( $this, 'get_images_json') );
57
-
58
- add_filter( 'the_content', array( $this, 'filter_post_images' ), 200 );
59
-
60
- if ( intval( get_option( 'bjll_filter_post_thumbnails', 1 ) ) ) {
61
- add_filter( 'post_thumbnail_html', array( $this, 'filter_post_thumbnail_html' ), 10 );
62
- }
63
- }
64
-
65
- public static function singleton() {
66
- if (!isset(self::$_instance)) {
67
- $className = __CLASS__;
68
- self::$_instance = new $className;
69
- }
70
- return self::$_instance;
71
- }
72
-
73
- public function enqueue_styles() {
74
- wp_enqueue_style( 'BJLL', plugins_url( '/css/bjll.css', __FILE__ ), array(), self::version );
75
- }
76
-
77
- public function enqueue_scripts() {
78
-
79
- $in_footer = true;
80
- $theme_caller = get_option( 'bjll_theme_caller' );
81
- if ( $theme_caller == 'wp_head' ) {
82
- $in_footer = false;
83
- }
84
-
85
- //wp_enqueue_script( 'JAIL', plugins_url( '/js/jail.min.js', __FILE__ ), array( 'jquery'), '0.9.7', true );
86
- //wp_enqueue_script( 'BJLL', plugins_url( '/js/bjll.js', __FILE__ ), array( 'jquery', 'JAIL' ), self::version, true );
87
-
88
- wp_enqueue_script( 'BJLL', plugins_url( '/js/bjll.min.js', __FILE__ ), array( 'jquery' ), self::version, $in_footer );
89
-
90
- }
91
-
92
- public function output_js_options() {
93
- /*
94
- wp_localize_script( 'BJLL', 'BJLL', array(
95
- 'timeout' => get_option('bjll_timeout', 10),
96
- 'effect' => get_option('bjll_effect', 'fadeIn'),
97
- 'speed' => get_option('bjll_speed', 400),
98
- 'event' => get_option('bjll_event', 'load+scroll'),
99
- 'callback' => get_option('bjll_callback', ''),
100
- //'callbackAfterEachImage' => get_option('bjll_callbackAfterEachImage', ''),
101
- 'placeholder' => get_option('bjll_placeholder', ''),
102
- 'offset' => get_option('bjll_offset', 200),
103
- 'ignoreHiddenImages' => get_option('bjll_ignoreHiddenImages', 0),
104
- ) );
105
- */
106
- ?>
107
- <script type='text/javascript'>
108
- /* <![CDATA[ */
109
- var BJLL = {
110
- options: {
111
- timeout: <?php echo intval( get_option( 'bjll_timeout', 10 ) ); ?>,
112
- effect: <?php echo ( strlen($val = get_option('bjll_effect', '')) ? '"' . $val . '"' : 'null' ); ?>,
113
- speed: <?php echo intval( get_option( 'bjll_speed', 400 ) ); ?>,
114
- event: "<?php echo get_option( 'bjll_event', 'load+scroll' ); ?>",
115
- callback: "<?php echo get_option( 'bjll_callback', '' ); ?>",
116
- placeholder: "<?php echo get_option( 'bjll_placeholder', '' ); ?>",
117
- offset: <?php echo intval( get_option( 'bjll_offset', '' ) ); ?>,
118
- ignoreHiddenImages: <?php echo intval( get_option( 'bjll_ignoreHiddenImages', 0 ) ); ?>
119
-
120
- },
121
- ajaxurl: "<?php echo admin_url( 'admin-ajax.php' ); ?>"
122
- };
123
- /* ]]> */
124
- </script>
125
- <?php
126
- }
127
-
128
- public function get_images_json() {
129
- echo json_encode( $_POST['attachmentIDs'] );
130
- exit;
131
- }
132
-
133
- public function filter_post_images( $content ) {
134
-
135
- $matches = array();
136
- preg_match_all( '/<img\s+.*?>/', $content, $matches );
137
-
138
- $search = array();
139
- $replace = array();
140
-
141
- foreach ( $matches[0] as $imgHTML ) {
142
-
143
- $replaceHTML = $this->_get_placeholder_html( $imgHTML );
144
-
145
- array_push( $search, $imgHTML );
146
- array_push( $replace, $replaceHTML );
147
- }
148
-
149
- $content = str_replace( $search, $replace, $content );
150
-
151
- return $content;
152
- }
153
-
154
- public function filter_post_thumbnail_html( $html ) {
155
-
156
- $html = $this->_get_placeholder_html( $html );
157
-
158
- return $html;
159
-
160
- }
161
-
162
- static function filter ( $html ) {
163
- $BJLL = BJLL::singleton();
164
- return $BJLL->get_placeholder_html ( $html );
165
- }
166
-
167
- public function get_placeholder_html ( $html ) {
168
- return $this->_get_placeholder_html ( $html );
169
- }
170
-
171
- protected function _get_placeholder_html ( $html ) {
172
-
173
- if ( class_exists( 'DOMDocument') ) {
174
- $html = $this->_get_placeholder_html_dom( $html );
175
- } else {
176
- $html = $this->_get_placeholder_html_regexp( $html );
177
- }
178
-
179
- return $html;
180
- }
181
-
182
- protected function _get_placeholder_html_dom ( $html ) {
183
-
184
- $loadhtml = sprintf( '<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><title></title></head><body>%s</body></html>', $html );
185
-
186
- $doc = @DOMDocument::loadHTML( $loadhtml );
187
- if ( ! $doc ) {
188
- return $this->_get_placeholder_html_regexp( $html );
189
- }
190
-
191
- $images = $doc->getElementsByTagName( 'img' );
192
-
193
- //$img = $images->item( 0 );
194
- // Thanks to d.sturm for pointing this out: http://wordpress.org/support/topic/plugin-bj-lazy-load?replies=3#post-2539962
195
- if ( ! $img = $images->item( 0 ) ) {
196
- return $this->_get_placeholder_html_regexp( $html );
197
- }
198
-
199
- //foreach ( $images as $img ) {
200
-
201
- $noscriptImg = $img->cloneNode( true );
202
- $noscript = $doc->createElement( 'noscript' );
203
- $noscript->appendChild( $noscriptImg );
204
-
205
- $src = $img->getAttribute( 'src' );
206
- $class = $img->getAttribute( 'class' );
207
-
208
- $class .= ' lazy lazy-hidden';
209
-
210
- $img->setAttribute( 'data-src' , $src );
211
- $img->setAttribute( 'src' , $this->_placeholder_url );
212
- $img->setAttribute( 'class' , trim( $class ) );
213
-
214
- $img->parentNode->appendChild( $noscript );
215
-
216
- //}
217
-
218
- $rethtml = $doc->saveHTML();
219
-
220
- $rethtml = substr( $rethtml, strpos( $rethtml, '<body>' ) + 6 );
221
- $rethtml = substr( $rethtml, 0, strpos( $rethtml, '</body>' ) );
222
-
223
- return $rethtml;
224
- }
225
- protected function _get_placeholder_html_regexp ( $html ) {
226
- $orig_html = $html;
227
-
228
- /**/
229
- // replace the src and add the data-src attribute
230
- $html = preg_replace( '/<img(.*?)src=/i', '<img$1src="' . $this->_placeholder_url . '" data-src=', $html );
231
-
232
- // add the lazy class to the img element
233
- if ( preg_match( '/class="/i', $html ) ) {
234
- $html = preg_replace( '/class="(.*?)"/i', ' class="lazy lazy-hidden $1"', $html );
235
- } else {
236
- $html = preg_replace( '/<img/i', '<img class="lazy lazy-hidden"', $html );
237
- }
238
-
239
- $html .= '<noscript>' . $orig_html . '</noscript>';
240
-
241
-
242
-
243
- // http://24ways.org/2011/adaptive-images-for-responsive-designs-again
244
- // This is a no-go. <img> within <noscript> within <a> gets parsed horribly wrong
245
- //$html = "<script>document.write('<' + '!--')</script><noscript class=\"lazy-nojs\">" . $orig_html . '<noscript -->';
246
-
247
- return $html;
248
- }
249
-
250
- }
251
-
252
- class BJLL_Admin {
253
-
254
- function __construct () {
255
- add_action( 'init', array( $this, 'load_i18n' ) );
256
- add_action( 'admin_menu', array( $this, 'plugin_menu' ) );
257
- add_action( 'admin_init', array( $this, 'register_settings' ) );
258
-
259
- add_filter( 'plugin_action_links', array( $this, 'plugin_settings_link' ), 10, 2);
260
- }
261
-
262
- function load_i18n() {;
263
- load_plugin_textdomain( 'bj-lazy-load', false, basename( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR . 'lang' );
264
- }
265
-
266
- function plugin_menu () {
267
- add_options_page( 'BJ Lazy Load', 'BJ Lazy Load', 'manage_options', 'bjll', array( $this, 'plugin_options_page' ) );
268
- }
269
-
270
- public static function plugin_settings_link( $links, $file ) {
271
-
272
- if ( plugin_basename( __FILE__ ) == $file ) {
273
- array_unshift($links, '<a href="' . admin_url( 'admin.php' ) . '?page=bjll">' . __( 'Settings' ) . '</a>');
274
- }
275
-
276
- return $links;
277
- }
278
-
279
- function register_settings () {
280
- register_setting( 'bjll_options', 'bjll_filter_post_thumbnails', 'intval' );
281
- register_setting( 'bjll_options', 'bjll_include_js', 'intval' );
282
- register_setting( 'bjll_options', 'bjll_include_css', 'intval' );
283
- register_setting( 'bjll_options', 'bjll_theme_caller' );
284
-
285
- add_settings_section( 'bjll_general', __('General'), array( 'BJLL_Admin', 'settings_section_general' ), 'bjll' );
286
-
287
- add_settings_field( 'bjll_filter_post_thumbnails', __( 'Lazy load post thumbnails', 'bj-lazy-load' ), array( 'BJLL_Admin', 'setting_field_filter_post_thumbnails' ), 'bjll', 'bjll_general', array( 'label_for' => 'bjll_filter_post_thumbnails' ) );
288
- add_settings_field( 'bjll_include_js', __( 'Include JS', 'bj-lazy-load' ), array( 'BJLL_Admin', 'setting_field_include_js'), 'bjll', 'bjll_general', array( 'label_for' => 'bjll_include_js' ) );
289
- add_settings_field( 'bjll_include_css', __( 'Include CSS', 'bj-lazy-load' ), array( 'BJLL_Admin', 'setting_field_include_css'), 'bjll', 'bjll_general', array( 'label_for' => 'bjll_include_css' ) );
290
- add_settings_field( 'bjll_theme_caller', __( 'Theme caller function', 'bj-lazy-load' ), array('BJLL_Admin', 'setting_field_theme_caller' ), 'bjll', 'bjll_general', array( 'label_for' => 'bjll_theme_caller' ) );
291
-
292
- register_setting( 'bjll_options', 'bjll_timeout', 'intval' );
293
- register_setting( 'bjll_options', 'bjll_effect' );
294
- register_setting( 'bjll_options', 'bjll_speed', 'intval' );
295
- register_setting( 'bjll_options', 'bjll_event', array( 'BJLL_Admin', 'sanitize_setting_event' ) );
296
- register_setting( 'bjll_options', 'bjll_callback' );
297
- register_setting( 'bjll_options', 'bjll_callbackAfterEachImage' );
298
- register_setting( 'bjll_options', 'bjll_placeholder' );
299
- register_setting( 'bjll_options', 'bjll_offset', 'intval' );
300
- register_setting( 'bjll_options', 'bjll_ignoreHiddenImages', 'intval' );
301
-
302
- add_settings_section( 'bjll_loader', __( 'JAIL Settings', 'bj-lazy-load' ), array( 'BJLL_Admin','settings_section_loader' ), 'bjll' );
303
-
304
- add_settings_field( 'bjll_timeout', __( 'Timeout', 'bj-lazy-load' ), array( 'BJLL_Admin', 'setting_field_timeout' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_timeout' ) );
305
- add_settings_field( 'bjll_effect', __( 'jQuery Effect', 'bj-lazy-load' ), array('BJLL_Admin', 'setting_field_effect' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_effect' ) );
306
- add_settings_field( 'bjll_speed', __( 'Effect Speed', 'bj-lazy-load' ), array('BJLL_Admin', 'setting_field_speed' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_speed' ) );
307
- add_settings_field( 'bjll_event', __( 'Trigger Event', 'bj-lazy-load' ), array('BJLL_Admin', 'setting_field_event' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_event' ) );
308
- add_settings_field( 'bjll_offset', __( 'Offset/Threshold', 'bj-lazy-load' ), array('BJLL_Admin', 'setting_field_offset' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_offset' ) );
309
- add_settings_field( 'bjll_ignoreHiddenImages', __( 'Ignore Hidden Images', 'bj-lazy-load' ), array( 'BJLL_Admin', 'setting_field_ignoreHiddenImages' ), 'bjll', 'bjll_loader', array( 'label_for' => 'bjll_ignoreHiddenImages' ) );
310
-
311
- }
312
-
313
- function sanitize_setting_theme_caller ( $val ) {
314
- $validoptions = self::_get_valid_setting_options_event();
315
- if ( ! in_array( $val, $validoptions ) ) {
316
- // get previous saved value
317
- $val = get_option( 'bjll_theme_caller', 'wp_footer' );
318
- if ( ! in_array( $val, $validoptions ) ) {
319
- // if still not valid, set to our default
320
- $val = $validoptions[0];
321
- }
322
- }
323
- return $val;
324
- }
325
- function sanitize_setting_event ( $val ) {
326
- $validoptions = self::_get_valid_setting_options_event();
327
- if ( ! in_array( $val, $validoptions ) ) {
328
- // get previous saved value
329
- $val = get_option( 'bjll_event', 'load+scroll' );
330
- if ( ! in_array( $val, $validoptions ) ) {
331
- // if still not valid, set to our default
332
- $val = $validoptions[0];
333
- }
334
- }
335
- return $val;
336
- }
337
-
338
- function sanitize_setting_effect ( $val ) {
339
- if ( ! strlen( $val ) ) {
340
- $val = null;
341
- }
342
- return $val;
343
- }
344
-
345
- private static function _get_valid_setting_options_theme_caller () {
346
- return array( 'wp_footer', 'wp_head' );
347
- }
348
- private static function _get_valid_setting_options_event () {
349
- return array( 'load+scroll', 'load', 'click', 'mouseover', 'scroll' );
350
- }
351
-
352
-
353
- function settings_section_general () {
354
- }
355
-
356
- function settings_section_loader () {
357
- }
358
-
359
- function setting_field_filter_post_thumbnails () {
360
- $checked = '';
361
- if ( intval( get_option( 'bjll_filter_post_thumbnails', 1 ) ) ) {
362
- $checked = ' checked="checked"';
363
- }
364
-
365
- echo '<input id="bjll_filter_post_thumbnails" name="bjll_filter_post_thumbnails" type="checkbox" value="1" ' . $checked . ' />';
366
- }
367
-
368
- function setting_field_include_js () {
369
- $checked = '';
370
- if ( intval( get_option( 'bjll_include_js', 1 ) ) ) {
371
- $checked = ' checked="checked"';
372
- }
373
-
374
- echo '<input id="bjll_include_js" name="bjll_include_js" type="checkbox" value="1" ' . $checked . ' /> ';
375
- _e( 'Needed for the plugin to work, but <a href="http://developer.yahoo.com/performance/rules.html#num_http" target="_blank">for best performance you should include it in your combined JS</a>', 'bj-lazy-load' );
376
- }
377
-
378
- function setting_field_include_css () {
379
- $checked = '';
380
- if ( intval( get_option( 'bjll_include_css', 1 ) ) ) {
381
- $checked = ' checked="checked"';
382
- }
383
-
384
- echo '<input id="bjll_include_css" name="bjll_include_css" type="checkbox" value="1" ' . $checked . ' /> ';
385
- _e( 'Needed for the plugin to work, but <a href="http://developer.yahoo.com/performance/rules.html#num_http" target="_blank">for best performance you should include it in your combined CSS</a>' , 'bj-lazy-load');
386
- }
387
- function setting_field_ignoreHiddenImages () {
388
-
389
- $checked = '';
390
- if ( intval( get_option( 'bjll_ignoreHiddenImages', 0 ) ) ) {
391
- $checked = ' checked="checked"';
392
- }
393
-
394
- echo '<input id="bjll_ignoreHiddenImages" name="bjll_ignoreHiddenImages" type="checkbox" value="1" ' . $checked . ' /> ';
395
- _e( 'Whether to ignore hidden images to be loaded - Default: false/unchecked (so hidden images are loaded)', 'bj-lazy-load' );
396
-
397
- }
398
- function setting_field_theme_caller () {
399
-
400
- $options = self::_get_valid_setting_options_theme_caller();
401
-
402
- $currentval = get_option( 'bjll_theme_caller' );
403
-
404
- echo '<select id="bjll_theme_caller" name="bjll_theme_caller">';
405
- foreach ( $options as $option ) {
406
- $selected = '';
407
- if ( $option == $currentval ) {
408
- $selected = ' selected="selected"';
409
- }
410
- echo sprintf( '<option value="%1$s"%2$s>%1$s</option>', $option, $selected );
411
- }
412
- echo '</select> ';
413
- _e( 'Put the script in either wp_footer() (should be right before &lt;/body&gt;) or wp_head() (in the &lt;head&gt;-element).', 'bj-lazy-load' );
414
- }
415
- function setting_field_event () {
416
-
417
- $options = self::_get_valid_setting_options_event();
418
-
419
- $currentval = get_option( 'bjll_event' );
420
-
421
- echo '<select id="bjll_event" name="bjll_event">';
422
- foreach ( $options as $option ) {
423
- $selected = '';
424
- if ( $option == $currentval ) {
425
- $selected = ' selected="selected"';
426
- }
427
- echo sprintf( '<option value="%1$s"%2$s>%1$s</option>', $option, $selected );
428
- }
429
- echo '</select> ';
430
- _e( 'Event that triggers the image to load. Default: load+scroll', 'bj-lazy-load' );
431
- }
432
- function setting_field_timeout () {
433
- $val = get_option( 'bjll_timeout', 10 );
434
- echo '<input id="bjll_timeout" name="bjll_timeout" type="text" value="' . $val . '" /> ';
435
- _e( 'Number of msec after that the images will be loaded - Default: 10', 'bj-lazy-load' );
436
- }
437
- function setting_field_effect () {
438
- $val = get_option( 'bjll_effect', '' );
439
- if ( 'null' == strtolower( $val ) ) {
440
- $val = '';
441
- }
442
- echo '<input id="bjll_effect" name="bjll_effect" type="text" value="' . $val . '" />';
443
- _e( 'Any jQuery effect that makes the images display (e.g. "fadeIn") - Default: NULL', 'bj-lazy-load');
444
- echo '<p>';
445
- _e( 'NOTE: If you are loading a large number of images, it is best to NOT use this setting. Effects calls are very expensive. Even a simple show() can have a major impact on the browser&rsquo;s responsiveness.', 'bj-lazy-load' );
446
- echo '</p>';
447
- }
448
- function setting_field_speed () {
449
- $val = get_option( 'bjll_speed', 400 );
450
- echo '<input id="bjll_speed" name="bjll_speed" type="text" value="' . $val . '" /> ';
451
- _e( 'string or number determining how long the animation will run - Default: 400', 'bj-lazy-load' );
452
- }
453
- function setting_field_offset () {
454
- $val = get_option( 'bjll_offset', 200 );
455
- echo '<input id="bjll_offset" name="bjll_offset" type="text" value="' . $val . '" /> ';
456
- _e( 'An offset of "500" would cause any images that are less than 500px below the bottom of the window or 500px above the top of the window to load. - Default: 200', 'bj-lazy-load' );
457
- }
458
-
459
- function plugin_options_page () {
460
- if ( ! current_user_can( 'manage_options' ) ) {
461
- wp_die( __( 'You do not have sufficient permissions to access this page.', 'bj-lazy-load' ) );
462
- }
463
- ?>
464
- <div class="wrap">
465
- <h2>BJ Lazy Load <?php _e( 'Settings' ); ?></h2>
466
- <form method="post" action="options.php">
467
- <?php settings_fields( 'bjll_options' ); ?>
468
- <?php do_settings_sections( 'bjll' ); ?>
469
-
470
- <p class="submit">
471
- <input type="submit" class="button-primary" value="<?php _e( 'Save Changes' ) ?>" />
472
- </p>
473
- </form>
474
- </div>
475
- <?php
476
- }
477
-
478
- }
479
-
480
-
481
- /*
482
- is_admin() will return true when trying to make an ajax request
483
- if (!is_admin() && !is_feed()) {
484
- */
485
- /* 'Conditional query tags do not work before the query is run. Before then, they always return false.' */
486
- /* Anonymous functions aren't available before PHP 5.3, so we need a temp wrapper */
487
- function BJLL_action_init () {
488
- if ( ! is_feed() ) {
489
- BJLL::singleton() ;
490
- }
491
- }
492
- add_action( 'wp', 'BJLL_action_init', 10, 0 );
493
-
494
-
495
- if ( is_admin() ) {
496
- new BJLL_Admin;
497
- }
498
-
1
+ <?php
2
+ /*
3
+ Plugin Name: BJ Lazy Load
4
+ Plugin URI: http://wordpress.org/extend/plugins/bj-lazy-load/
5
+ Description: Lazy image loading makes your site load faster and saves bandwidth.
6
+ Version: 0.5.0
7
+ Author: Bjørn Johansen
8
+ Author URI: http://twitter.com/bjornjohansen
9
+ License: GPL2
10
+
11
+ Copyright 2011–2012 Bjørn Johansen (email : post@bjornjohansen.no)
12
+
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License, version 2, as
15
+ published by the Free Software Foundation.
16
+
17
+ This program is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with this program; if not, write to the Free Software
24
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
+
26
+ */
27
+
28
+
29
+ require_once( dirname(__FILE__) . '/scb/load.php' );
30
+
31
+ if ( ! class_exists( 'BJLL' ) ) {
32
+ class BJLL {
33
+
34
+ const version = '0.5.0';
35
+ protected $_placeholder_url;
36
+
37
+ protected static $_instance;
38
+
39
+ function __construct() {
40
+
41
+ $this->_placeholder_url = plugins_url( '/img/placeholder.gif', __FILE__ );
42
+
43
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
44
+
45
+ add_filter( 'the_content', array( $this, 'filter' ), 200 );
46
+ add_filter( 'post_thumbnail_html', array( $this, 'filter' ), 200 );
47
+ add_filter( 'get_avatar', array( $this, 'filter' ), 200 );
48
+ }
49
+
50
+ static function singleton() {
51
+ if ( ! isset( self::$_instance ) ) {
52
+ $className = __CLASS__;
53
+ self::$_instance = new $className;
54
+ }
55
+ return self::$_instance;
56
+ }
57
+
58
+ static function enqueue_scripts() {
59
+
60
+ $in_footer = true;
61
+
62
+ $options = self::_get_options();
63
+ $theme_loader_function = $options->get( 'theme_loader_function' );
64
+
65
+ if ( $theme_loader_function == 'wp_head' ) {
66
+ $in_footer = false;
67
+ }
68
+
69
+ wp_enqueue_script( 'jquery.sonar', plugins_url( '/js/jquery.sonar.min.js', __FILE__ ), array( 'jquery' ), self::version, $in_footer );
70
+ wp_enqueue_script( 'BJLL', plugins_url( '/js/bj-lazy-load.min.js', __FILE__ ), array( 'jquery', 'jquery.sonar' ), self::version, $in_footer );
71
+ }
72
+
73
+ static function filter( $content ) {
74
+
75
+ $BJLL = BJLL::singleton();
76
+
77
+ $options = self::_get_options();
78
+
79
+ if ( $options->get('lazy_load_images') == 'yes' ) {
80
+ $content = $BJLL->_filter_images( $content );
81
+ }
82
+
83
+ if ( $options->get('lazy_load_iframes') == 'yes' ) {
84
+ $content = $BJLL->_filter_iframes( $content );
85
+ }
86
+
87
+ return $content;
88
+ }
89
+
90
+ protected function _filter_images( $content ) {
91
+
92
+ $matches = array();
93
+ preg_match_all( '/<img\s+.*?>/', $content, $matches );
94
+
95
+ $search = array();
96
+ $replace = array();
97
+
98
+ foreach ( $matches[0] as $imgHTML ) {
99
+
100
+ // replace the src and add the data-src attribute
101
+ $replaceHTML = preg_replace( '/<img(.*?)src=/i', '<img$1src="' . $this->_placeholder_url . '" data-lazy-type="image" data-lazy-src=', $imgHTML );
102
+
103
+ // add the lazy class to the img element
104
+ if ( preg_match( '/class="/i', $replaceHTML ) ) {
105
+ $replaceHTML = preg_replace( '/class="(.*?)"/i', 'class="lazy lazy-hidden $1"', $replaceHTML );
106
+ } else {
107
+ $replaceHTML = preg_replace( '/<img/i', '<img class="lazy lazy-hidden"', $replaceHTML );
108
+ }
109
+
110
+ $replaceHTML .= '<noscript>' . $imgHTML . '</noscript>';
111
+
112
+ array_push( $search, $imgHTML );
113
+ array_push( $replace, $replaceHTML );
114
+ }
115
+
116
+ $content = str_replace( $search, $replace, $content );
117
+
118
+
119
+ return $content;
120
+ }
121
+
122
+ protected function _filter_iframes( $content ) {
123
+
124
+ $matches = array();
125
+ preg_match_all( '/<iframe\s+.*?>/', $content, $matches );
126
+
127
+ $search = array();
128
+ $replace = array();
129
+
130
+ foreach ( $matches[0] as $iframeHTML ) {
131
+ $replaceHTML = '<img src="' . $this->_placeholder_url . '" class="lazy lazy-hidden" data-lazy-type="iframe" data-lazy-src="' . base64_encode($iframeHTML) . '" alt="">';
132
+
133
+ $replaceHTML .= '<noscript>' . $iframeHTML . '</noscript>';
134
+
135
+ array_push( $search, $iframeHTML );
136
+ array_push( $replace, $replaceHTML );
137
+ }
138
+
139
+ $content = str_replace( $search, $replace, $content );
140
+
141
+ return $content;
142
+ }
143
+
144
+ protected function _get_options() {
145
+ return new scbOptions( 'bj_lazy_load_options', __FILE__, array(
146
+ 'lazy_load_images' => 'yes',
147
+ 'lazy_load_iframes' => 'yes',
148
+ 'theme_loader_function' => 'wp_footer'
149
+ ) );
150
+ }
151
+
152
+ function options_init() {
153
+
154
+ $options = self::_get_options();
155
+
156
+ // Creating settings page objects
157
+ if ( is_admin() ) {
158
+ require_once( dirname( __FILE__ ) . '/admin.php' );
159
+ new BJLL_Admin_Page( __FILE__, $options );
160
+ }
161
+ }
162
+
163
+ }
164
+ }
165
+
166
+
167
+ add_action( 'wp', create_function('', 'if ( ! is_feed() ) { BJLL::singleton(); }'), 10, 0 );
168
+
169
+ scb_init( array( 'BJLL', 'options_init' ) );
170
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/bjll.css DELETED
@@ -1 +0,0 @@
1
- .lazy-hidden {display: none !important;}
 
js/bj-lazy-load.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($) {
2
+
3
+ function bj_lazy_load_init() {
4
+
5
+ $('.lazy-hidden').not('[data-lazy-ready]').one( 'scrollin.bj_lazy_load', { distance: 200 }, function() {
6
+
7
+ var $el = $( this ),
8
+ data_lazy_type = $el.attr( 'data-lazy-type' );
9
+
10
+ if ( data_lazy_type == 'image' ) {
11
+ $el.attr( 'src', $el.attr( 'data-lazy-src' ) )
12
+ .removeClass( 'lazy-hidden' );
13
+ } else if ( data_lazy_type == 'iframe' ) {
14
+ $el.replaceWith( bj_lazy_load_base64_decode( $el.attr( 'data-lazy-src' ) ) );
15
+ }
16
+ }).attr( 'data-lazy-ready', 'true' );
17
+
18
+ }
19
+
20
+ function bj_lazy_load_base64_decode (data) {
21
+ // http://kevin.vanzonneveld.net
22
+ // + original by: Tyler Akins (http://rumkin.com)
23
+ // + improved by: Thunder.m
24
+ // + input by: Aman Gupta
25
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
26
+ // + bugfixed by: Onno Marsman
27
+ // + bugfixed by: Pellentesque Malesuada
28
+ // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
29
+ // + input by: Brett Zamir (http://brett-zamir.me)
30
+ // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
31
+ // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
32
+ // * returns 1: 'Kevin van Zonneveld'
33
+ // mozilla has this native
34
+ // - but breaks in 2.0.0.12!
35
+ //if (typeof this.window['atob'] == 'function') {
36
+ // return atob(data);
37
+ //}
38
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
39
+ var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
40
+ ac = 0,
41
+ dec = "",
42
+ tmp_arr = [];
43
+
44
+ if (!data) {
45
+ return data;
46
+ }
47
+
48
+ data += '';
49
+
50
+ do { // unpack four hexets into three octets using index points in b64
51
+ h1 = b64.indexOf(data.charAt(i++));
52
+ h2 = b64.indexOf(data.charAt(i++));
53
+ h3 = b64.indexOf(data.charAt(i++));
54
+ h4 = b64.indexOf(data.charAt(i++));
55
+
56
+ bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
57
+
58
+ o1 = bits >> 16 & 0xff;
59
+ o2 = bits >> 8 & 0xff;
60
+ o3 = bits & 0xff;
61
+
62
+ if (h3 == 64) {
63
+ tmp_arr[ac++] = String.fromCharCode(o1);
64
+ } else if (h4 == 64) {
65
+ tmp_arr[ac++] = String.fromCharCode(o1, o2);
66
+ } else {
67
+ tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
68
+ }
69
+ } while (i < data.length);
70
+
71
+ dec = tmp_arr.join('');
72
+
73
+ return dec;
74
+ }
75
+
76
+ $( document ).on( 'ready', bj_lazy_load_init );
77
+
78
+ })(jQuery);
js/bj-lazy-load.min.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(c){function b(){c(".lazy-hidden").not("[data-lazy-ready]").one("scrollin.bj_lazy_load",{distance:200},function(){var d=c(this),e=d.attr("data-lazy-type");if(e=="image"){d.attr("src",d.attr("data-lazy-src")).removeClass("lazy-hidden")}else{if(e=="iframe"){d.replaceWith(a(d.attr("data-lazy-src")))}}}).attr("data-lazy-ready","true")}function a(l){var g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var f,e,d,p,o,n,m,q,k=0,r=0,h="",j=[];if(!l){return l}l+="";do{p=g.indexOf(l.charAt(k++));o=g.indexOf(l.charAt(k++));n=g.indexOf(l.charAt(k++));m=g.indexOf(l.charAt(k++));q=p<<18|o<<12|n<<6|m;f=q>>16&255;e=q>>8&255;d=q&255;if(n==64){j[r++]=String.fromCharCode(f)}else{if(m==64){j[r++]=String.fromCharCode(f,e)}else{j[r++]=String.fromCharCode(f,e,d)}}}while(k<l.length);h=j.join("");return h}c(document).on("ready",b)})(jQuery);
js/bjll.js DELETED
@@ -1,5 +0,0 @@
1
- (function($) {
2
- $(document).ready(function() {
3
- $('img.lazy').removeClass('lazy-hidden').jail(BJLL.options);
4
- });
5
- })(jQuery);
 
 
 
 
 
js/bjll.min.js DELETED
@@ -1,22 +0,0 @@
1
- /*
2
- * JAIL: jQuery Asynchronous Image Loader
3
- *
4
- * Copyright (c) 2011 Sebastiano Armeli-Battana (http://www.sebastianoarmelibattana.com)
5
- *
6
- * By Sebastiano Armeli-Battana (@sebarmeli) - http://www.sebastianoarmelibattana.com
7
- * Licensed under the MIT license.
8
- * https://github.com/sebarmeli/JAIL/blob/master/MIT-LICENSE.txt
9
- *
10
- * Tested with jQuery 1.3.2+ on FF 2+, Opera 10+, Safari 4+, Chrome 8+ on Win/Mac/Linux and IE 6/7/8 on Win.
11
- *
12
- * Contributor : Derek Lindahl - dlindahl
13
- *
14
- * @link http://github.com/sebarmeli/JAIL
15
- * @author Sebastiano Armeli-Battana
16
- * @date 30/12/2011
17
- * @version 0.9.9
18
- *
19
- */
20
- (function(a,c){var b=c(jQuery),d=typeof define==="function"&&define.amd;if(d){define("jail",["jquery"],b);}else{(this.jQuery||this.$||this)[a]=b;}}("jail",function(f){var b=f(window),d={timeout:1,effect:false,speed:400,triggerElement:null,offset:0,event:"load",callback:null,callbackAfterEachImage:null,placeholder:false,loadHiddenImages:false},k=[],g=false;f.jail=function(o,n){var o=o||{},n=f.extend({},d,n);f.jail.prototype.init(o,n);if(/^(load|scroll)/.test(n.event)){f.jail.prototype.later.call(o,n);}else{f.jail.prototype.onEvent.call(o,n);}};f.jail.prototype.init=function(o,n){o.data("triggerEl",(n.triggerElement)?f(n.triggerElement):b);if(!!n.placeholder){o.each(function(){f(this).attr("src",n.placeholder);});}};f.jail.prototype.onEvent=function(o){var n=this;if(!!o.triggerElement){i(o,n);}else{n.bind(o.event,{options:o,images:n},function(s){var r=f(this),q=s.data.options,p=s.data.images;k=f.extend({},p);a(q,r);f(s.currentTarget).unbind(s.type);});}};f.jail.prototype.later=function(o){var n=this;setTimeout(function(){k=f.extend({},n);n.each(function(){c(o,this,n);});o.event="scroll";i(o,n);},o.timeout);};function i(o,n){if(!!n){var p=n.data("triggerEl");}if(!!p&&typeof p.bind==="function"){p.bind(o.event,{options:o,images:n},m);b.resize({options:o,images:n},m);}}function j(n){var o=0;if(n.length>0){while(true){if(o===n.length){break;}else{if(n[o].getAttribute("data-src")){o++;}else{n.splice(o,1);}}}}}function m(p){var n=p.data.images,o=p.data.options;n.data("poller",setTimeout(function(){k=f.extend({},n);j(k);f(k).each(function(){if(this===window){return;}c(o,this,k);});if(l(k)){f(p.currentTarget).unbind(p.type);return;}else{if(o.event!=="scroll"){var q=(/scroll/i.test(o.event))?n.data("triggerEl"):b;o.event="scroll";n.data("triggerEl",q);i(o,f(k));}}},o.timeout));}function l(n){var o=true;f(n).each(function(){if(!!f(this).attr("data-src")){o=false;}});return o;}function c(q,s,o){var r=f(s),p=(/scroll/i.test(q.event))?o.data("triggerEl"):b,n=true;if(!q.loadHiddenImages){n=h(r,p,q)&&r.is(":visible");}if(n&&e(p,r,q.offset)){a(q,r);}}function e(u,n,s){var q=u[0]===window,y=(q?{top:0,left:0}:u.offset()),r=y.top+(q?u.scrollTop():0),t=y.left+(q?u.scrollLeft():0),p=t+u.width(),v=r+u.height(),x=n.offset(),w=n.width(),o=n.height();return(r-s)<=(x.top+o)&&(v+s)>=x.top&&(t-s)<=(x.left+w)&&(p+s)>=x.left;}function a(n,o){o.hide();o.attr("src",o.attr("data-src"));o.removeAttr("data-src");if(n.effect){if(n.speed){o[n.effect](n.speed);}else{o[n.effect]();}o.css("opacity",1);o.show();}else{o.show();}j(k);if(!!n.callbackAfterEachImage){n.callbackAfterEachImage.call(this,o,n);}if(l(k)&&!!n.callback&&!g){n.callback.call(f.jail,n);g=true;}}function h(q,o,p){var r=q.parent(),n=true;while(r.get(0).nodeName.toUpperCase()!=="BODY"){if(r.css("overflow")==="hidden"){if(!e(r,q,p.offset)){n=false;break;}}else{if(r.css("overflow")==="scroll"){if(!e(r,q,p.offset)){n=false;f(k).data("triggerEl",r);p.event="scroll";i(p,f(k));break;}}}if(r.css("visibility")==="hidden"||q.css("visibility")==="hidden"){n=false;break;}if(o!==b&&r===o){break;}r=r.parent();}return n;}f.fn.jail=function(n){new f.jail(this,n);k=[];return this;};return f.jail;}));
21
- /* bjll.js by Bjørn Johansen (@bjornjohansen) */
22
- (function($){$(document).ready(function(){$('img.lazy').removeClass('lazy-hidden').jail(BJLL.options);});})(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/jail.min.js DELETED
@@ -1,20 +0,0 @@
1
- /*
2
- * JAIL: jQuery Asynchronous Image Loader
3
- *
4
- * Copyright (c) 2011 Sebastiano Armeli-Battana (http://www.sebastianoarmelibattana.com)
5
- *
6
- * By Sebastiano Armeli-Battana (@sebarmeli) - http://www.sebastianoarmelibattana.com
7
- * Licensed under the MIT license.
8
- * https://github.com/sebarmeli/JAIL/blob/master/MIT-LICENSE.txt
9
- *
10
- * Tested with jQuery 1.3.2+ on FF 2+, Opera 10+, Safari 4+, Chrome 8+ on Win/Mac/Linux and IE 6/7/8 on Win.
11
- *
12
- * Contributor : Derek Lindahl - dlindahl
13
- *
14
- * @link http://github.com/sebarmeli/JAIL
15
- * @author Sebastiano Armeli-Battana
16
- * @date 30/12/2011
17
- * @version 0.9.9
18
- *
19
- */
20
- (function(a,c){var b=c(jQuery),d=typeof define==="function"&&define.amd;if(d){define("jail",["jquery"],b);}else{(this.jQuery||this.$||this)[a]=b;}}("jail",function(f){var b=f(window),d={timeout:1,effect:false,speed:400,triggerElement:null,offset:0,event:"load",callback:null,callbackAfterEachImage:null,placeholder:false,loadHiddenImages:false},k=[],g=false;f.jail=function(o,n){var o=o||{},n=f.extend({},d,n);f.jail.prototype.init(o,n);if(/^(load|scroll)/.test(n.event)){f.jail.prototype.later.call(o,n);}else{f.jail.prototype.onEvent.call(o,n);}};f.jail.prototype.init=function(o,n){o.data("triggerEl",(n.triggerElement)?f(n.triggerElement):b);if(!!n.placeholder){o.each(function(){f(this).attr("src",n.placeholder);});}};f.jail.prototype.onEvent=function(o){var n=this;if(!!o.triggerElement){i(o,n);}else{n.bind(o.event,{options:o,images:n},function(s){var r=f(this),q=s.data.options,p=s.data.images;k=f.extend({},p);a(q,r);f(s.currentTarget).unbind(s.type);});}};f.jail.prototype.later=function(o){var n=this;setTimeout(function(){k=f.extend({},n);n.each(function(){c(o,this,n);});o.event="scroll";i(o,n);},o.timeout);};function i(o,n){if(!!n){var p=n.data("triggerEl");}if(!!p&&typeof p.bind==="function"){p.bind(o.event,{options:o,images:n},m);b.resize({options:o,images:n},m);}}function j(n){var o=0;if(n.length>0){while(true){if(o===n.length){break;}else{if(n[o].getAttribute("data-src")){o++;}else{n.splice(o,1);}}}}}function m(p){var n=p.data.images,o=p.data.options;n.data("poller",setTimeout(function(){k=f.extend({},n);j(k);f(k).each(function(){if(this===window){return;}c(o,this,k);});if(l(k)){f(p.currentTarget).unbind(p.type);return;}else{if(o.event!=="scroll"){var q=(/scroll/i.test(o.event))?n.data("triggerEl"):b;o.event="scroll";n.data("triggerEl",q);i(o,f(k));}}},o.timeout));}function l(n){var o=true;f(n).each(function(){if(!!f(this).attr("data-src")){o=false;}});return o;}function c(q,s,o){var r=f(s),p=(/scroll/i.test(q.event))?o.data("triggerEl"):b,n=true;if(!q.loadHiddenImages){n=h(r,p,q)&&r.is(":visible");}if(n&&e(p,r,q.offset)){a(q,r);}}function e(u,n,s){var q=u[0]===window,y=(q?{top:0,left:0}:u.offset()),r=y.top+(q?u.scrollTop():0),t=y.left+(q?u.scrollLeft():0),p=t+u.width(),v=r+u.height(),x=n.offset(),w=n.width(),o=n.height();return(r-s)<=(x.top+o)&&(v+s)>=x.top&&(t-s)<=(x.left+w)&&(p+s)>=x.left;}function a(n,o){o.hide();o.attr("src",o.attr("data-src"));o.removeAttr("data-src");if(n.effect){if(n.speed){o[n.effect](n.speed);}else{o[n.effect]();}o.css("opacity",1);o.show();}else{o.show();}j(k);if(!!n.callbackAfterEachImage){n.callbackAfterEachImage.call(this,o,n);}if(l(k)&&!!n.callback&&!g){n.callback.call(f.jail,n);g=true;}}function h(q,o,p){var r=q.parent(),n=true;while(r.get(0).nodeName.toUpperCase()!=="BODY"){if(r.css("overflow")==="hidden"){if(!e(r,q,p.offset)){n=false;break;}}else{if(r.css("overflow")==="scroll"){if(!e(r,q,p.offset)){n=false;f(k).data("triggerEl",r);p.event="scroll";i(p,f(k));break;}}}if(r.css("visibility")==="hidden"||q.css("visibility")==="hidden"){n=false;break;}if(o!==b&&r===o){break;}r=r.parent();}return n;}f.fn.jail=function(n){new f.jail(this,n);k=[];return this;};return f.jail;}));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/jquery.sonar.min.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(e,h,l,c){e.fn.sonar=function(o,n){if(typeof o==="boolean"){n=o;o=c}return e.sonar(this[0],o,n)};var f=l.body,a="scrollin",m="scrollout",b=function(r,n,t){if(r){f||(f=l.body);var s=r,u=0,v=f.offsetHeight,o=h.innerHeight||l.documentElement.clientHeight||f.clientHeight||0,q=l.documentElement.scrollTop||h.pageYOffset||f.scrollTop||0,p=r.offsetHeight||0;if(!r.sonarElemTop||r.sonarBodyHeight!==v){if(s.offsetParent){do{u+=s.offsetTop}while(s=s.offsetParent)}r.sonarElemTop=u;r.sonarBodyHeight=v}n=n===c?0:n;return(!(r.sonarElemTop+(t?0:p)<q-n)&&!(r.sonarElemTop+(t?p:0)>q+o+n))}},d={},j=0,i=function(){setTimeout(function(){var s,o,t,q,p,r,n;for(t in d){o=d[t];for(r=0,n=o.length;r<n;r++){q=o[r];s=q.elem;p=b(s,q.px,q.full);if(t===m?!p:p){if(!q.tr){if(s[t]){e(s).trigger(t);q.tr=1}else{o.splice(r,1);r--;n--}}}else{q.tr=0}}}},25)},k=function(n,o){n[o]=0},g=function(r,p){var t=p.px,q=p.full,s=p.evt,o=b(r,t,q),n=0;r[s]=1;if(s===m?!o:o){setTimeout(function(){e(r).trigger(s===m?m:a)},0);n=1}d[s].push({elem:r,px:t,full:q,tr:n});if(!j){e(h).bind("scroll",i);j=1}};e.sonar=b;d[a]=[];e.event.special[a]={add:function(n){var p=n.data||{},o=this;if(!o[a]){g(this,{px:p.distance,full:p.full,evt:a})}},remove:function(n){k(this,a)}};d[m]=[];e.event.special[m]={add:function(n){var p=n.data||{},o=this;if(!o[m]){g(o,{px:p.distance,full:p.full,evt:m})}},remove:function(n){k(this,m)}}})(jQuery,window,document);
lang/bj-lazy-load-nb_NO.mo DELETED
Binary file
lang/bj-lazy-load-nb_NO.po DELETED
@@ -1,137 +0,0 @@
1
- # Copyright (C) 2010 BJ Lazy Load
2
- # This file is distributed under the same license as the BJ Lazy Load package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: BJ Lazy Load 0.2.4\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/bj-lazy-load\n"
7
- "POT-Creation-Date: 2012-04-04 23:30:34+00:00\n"
8
- "PO-Revision-Date: 2012-04-05 01:34+0100\n"
9
- "Last-Translator: Bjørn Johansen <post@bjornjohansen.no>\n"
10
- "Language-Team: <post@bjornjohansen.no>\n"
11
- "MIME-Version: 1.0\n"
12
- "Content-Type: text/plain; charset=UTF-8\n"
13
- "Content-Transfer-Encoding: 8bit\n"
14
- "X-Poedit-Language: Norwegian Bokmal\n"
15
- "X-Poedit-Country: NORWAY\n"
16
-
17
- #: bj-lazy-load.php:273
18
- #: bj-lazy-load.php:465
19
- msgid "Settings"
20
- msgstr "Innstillinger"
21
-
22
- #: bj-lazy-load.php:285
23
- msgid "General"
24
- msgstr "Generelt"
25
-
26
- #: bj-lazy-load.php:287
27
- msgid "Lazy load post thumbnails"
28
- msgstr "Lat lasting av fremhevede bilder"
29
-
30
- #: bj-lazy-load.php:288
31
- msgid "Include JS"
32
- msgstr "Inkludér JS"
33
-
34
- #: bj-lazy-load.php:289
35
- msgid "Include CSS"
36
- msgstr "Inkludér CSS"
37
-
38
- #: bj-lazy-load.php:290
39
- msgid "Theme caller function"
40
- msgstr "Kallfunksjon i tema"
41
-
42
- #: bj-lazy-load.php:302
43
- msgid "JAIL Settings"
44
- msgstr "JAIL-innstillinger"
45
-
46
- #: bj-lazy-load.php:304
47
- msgid "Timeout"
48
- msgstr "Tidsterskel"
49
-
50
- #: bj-lazy-load.php:305
51
- msgid "jQuery Effect"
52
- msgstr "jQuery-effekt"
53
-
54
- #: bj-lazy-load.php:306
55
- msgid "Effect Speed"
56
- msgstr "Effekthastighet"
57
-
58
- #: bj-lazy-load.php:307
59
- msgid "Trigger Event"
60
- msgstr "Handlingsutløser"
61
-
62
- #: bj-lazy-load.php:308
63
- msgid "Offset/Threshold"
64
- msgstr "Avstand/terskel"
65
-
66
- #: bj-lazy-load.php:309
67
- msgid "Ignore Hidden Images"
68
- msgstr "Ignorer skjulte bilder"
69
-
70
- #: bj-lazy-load.php:375
71
- msgid "Needed for the plugin to work, but <a href=\"http://developer.yahoo.com/performance/rules.html#num_http\" target=\"_blank\">for best performance you should include it in your combined JS</a>"
72
- msgstr "Nødvendig for at innstikket skal fungere, men <a href=\"http://developer.yahoo.com/performance/rules.html#num_http\" target=\"_blank\">for best ytelse bør du inkludere det i din kombinerte JS-fil</a>"
73
-
74
- #: bj-lazy-load.php:385
75
- msgid "Needed for the plugin to work, but <a href=\"http://developer.yahoo.com/performance/rules.html#num_http\" target=\"_blank\">for best performance you should include it in your combined CSS</a>"
76
- msgstr "Nødvendig for at innstikket skal fungere, men <a href=\"http://developer.yahoo.com/performance/rules.html#num_http\" target=\"_blank\">for best ytelse bør du inkludere det i din kombinerte CSS-fil</a>"
77
-
78
- #: bj-lazy-load.php:395
79
- msgid "Whether to ignore hidden images to be loaded - Default: false/unchecked (so hidden images are loaded)"
80
- msgstr "Om skjulte bilder skal ignoreres - Standard: usann/ikke valgt (slik at skjulte bilder også lastes)"
81
-
82
- #: bj-lazy-load.php:413
83
- msgid "Put the script in either wp_footer() (should be right before &lt;/body&gt;) or wp_head() (in the &lt;head&gt;-element)."
84
- msgstr "Legg skriptet enten i wp_footer() (bør være rett før &lt;/body&gt;) eller wp_head() (i &lt;head&gt;-elementet)."
85
-
86
- #: bj-lazy-load.php:430
87
- msgid "Event that triggers the image to load. Default: load+scroll"
88
- msgstr "Handling som utløser lasting av bilde. Standard: load+scroll"
89
-
90
- #: bj-lazy-load.php:435
91
- msgid "Number of msec after that the images will be loaded - Default: 10"
92
- msgstr "Antall msec etter handling som bildene lastes - Standard: 10"
93
-
94
- #: bj-lazy-load.php:443
95
- msgid "Any jQuery effect that makes the images display (e.g. \"fadeIn\") - Default: NULL"
96
- msgstr "Hvilken som helst jQuery-effekt som skal vise bildene (f.eks. \"fadeIn\") - Standard: NULL"
97
-
98
- #: bj-lazy-load.php:445
99
- msgid "NOTE: If you are loading a large number of images, it is best to NOT use this setting. Effects calls are very expensive. Even a simple show() can have a major impact on the browser&rsquo;s responsiveness."
100
- msgstr "MERK: Hvis du laster mange bilder, er det best å IKKE bruke denne innstillingen. Effektkall er veldig ressurskrevende. Til og med en enkel show() kan ha større innvirknign på nettleserens reaksjonsevne."
101
-
102
- #: bj-lazy-load.php:451
103
- msgid "string or number determining how long the animation will run - Default: 400"
104
- msgstr "streng eller tall som angir hvor lenge animasjonen skal kjøres - Standard: 400"
105
-
106
- #: bj-lazy-load.php:456
107
- msgid "An offset of \"500\" would cause any images that are less than 500px below the bottom of the window or 500px above the top of the window to load. - Default: 200"
108
- msgstr "En terskel på \"500\" vil laste bilder som er mindre enn 500px under nedre kant av vinduet eller 500px over øvre kant av vinduet. - Standard: 200"
109
-
110
- #: bj-lazy-load.php:461
111
- msgid "You do not have sufficient permissions to access this page."
112
- msgstr "Du har ikke tilstrekkelig tilgangsnivå til denne siden."
113
-
114
- #: bj-lazy-load.php:471
115
- msgid "Save Changes"
116
- msgstr "Lagre Endringer"
117
-
118
- #. Plugin Name of the plugin/theme
119
- msgid "BJ Lazy Load"
120
- msgstr "BJ Lazy Load"
121
-
122
- #. Plugin URI of the plugin/theme
123
- msgid "http://wordpress.org/extend/plugins/bj-lazy-load/"
124
- msgstr "http://wordpress.org/extend/plugins/bj-lazy-load/"
125
-
126
- #. Description of the plugin/theme
127
- msgid "Lazy image loading makes your site load faster and saves bandwidth."
128
- msgstr "Lat bildelasting gjør nettsteder raskere og sparer båndbredde."
129
-
130
- #. Author of the plugin/theme
131
- msgid "Bjørn Johansen"
132
- msgstr "Bjørn Johansen"
133
-
134
- #. Author URI of the plugin/theme
135
- msgid "http://twitter.com/bjornjohansen"
136
- msgstr "http://twitter.com/bjornjohansen"
137
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lang/bj-lazy-load.pot DELETED
@@ -1,152 +0,0 @@
1
- # Copyright (C) 2012 BJ Lazy Load
2
- # This file is distributed under the same license as the BJ Lazy Load package.
3
- msgid ""
4
- msgstr ""
5
- "Project-Id-Version: BJ Lazy Load 0.3.3\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/bj-lazy-load\n"
7
- "POT-Creation-Date: 2012-04-04 23:30:34+00:00\n"
8
- "MIME-Version: 1.0\n"
9
- "Content-Type: text/plain; charset=UTF-8\n"
10
- "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2012-MO-DA HO:MI+ZONE\n"
12
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
- "Language-Team: LANGUAGE <LL@li.org>\n"
14
-
15
- #: bj-lazy-load.php:273 bj-lazy-load.php:465
16
- msgid "Settings"
17
- msgstr ""
18
-
19
- #: bj-lazy-load.php:285
20
- msgid "General"
21
- msgstr ""
22
-
23
- #: bj-lazy-load.php:287
24
- msgid "Lazy load post thumbnails"
25
- msgstr ""
26
-
27
- #: bj-lazy-load.php:288
28
- msgid "Include JS"
29
- msgstr ""
30
-
31
- #: bj-lazy-load.php:289
32
- msgid "Include CSS"
33
- msgstr ""
34
-
35
- #: bj-lazy-load.php:290
36
- msgid "Theme caller function"
37
- msgstr ""
38
-
39
- #: bj-lazy-load.php:302
40
- msgid "JAIL Settings"
41
- msgstr ""
42
-
43
- #: bj-lazy-load.php:304
44
- msgid "Timeout"
45
- msgstr ""
46
-
47
- #: bj-lazy-load.php:305
48
- msgid "jQuery Effect"
49
- msgstr ""
50
-
51
- #: bj-lazy-load.php:306
52
- msgid "Effect Speed"
53
- msgstr ""
54
-
55
- #: bj-lazy-load.php:307
56
- msgid "Trigger Event"
57
- msgstr ""
58
-
59
- #: bj-lazy-load.php:308
60
- msgid "Offset/Threshold"
61
- msgstr ""
62
-
63
- #: bj-lazy-load.php:309
64
- msgid "Ignore Hidden Images"
65
- msgstr ""
66
-
67
- #: bj-lazy-load.php:375
68
- msgid ""
69
- "Needed for the plugin to work, but <a href=\"http://developer.yahoo.com/"
70
- "performance/rules.html#num_http\" target=\"_blank\">for best performance you "
71
- "should include it in your combined JS</a>"
72
- msgstr ""
73
-
74
- #: bj-lazy-load.php:385
75
- msgid ""
76
- "Needed for the plugin to work, but <a href=\"http://developer.yahoo.com/"
77
- "performance/rules.html#num_http\" target=\"_blank\">for best performance you "
78
- "should include it in your combined CSS</a>"
79
- msgstr ""
80
-
81
- #: bj-lazy-load.php:395
82
- msgid ""
83
- "Whether to ignore hidden images to be loaded - Default: false/unchecked (so "
84
- "hidden images are loaded)"
85
- msgstr ""
86
-
87
- #: bj-lazy-load.php:413
88
- msgid ""
89
- "Put the script in either wp_footer() (should be right before &lt;/body&gt;) "
90
- "or wp_head() (in the &lt;head&gt;-element)."
91
- msgstr ""
92
-
93
- #: bj-lazy-load.php:430
94
- msgid "Event that triggers the image to load. Default: load+scroll"
95
- msgstr ""
96
-
97
- #: bj-lazy-load.php:435
98
- msgid "Number of msec after that the images will be loaded - Default: 10"
99
- msgstr ""
100
-
101
- #: bj-lazy-load.php:443
102
- msgid ""
103
- "Any jQuery effect that makes the images display (e.g. \"fadeIn\") - Default: "
104
- "NULL"
105
- msgstr ""
106
-
107
- #: bj-lazy-load.php:445
108
- msgid ""
109
- "NOTE: If you are loading a large number of images, it is best to NOT use "
110
- "this setting. Effects calls are very expensive. Even a simple show() can "
111
- "have a major impact on the browser&rsquo;s responsiveness."
112
- msgstr ""
113
-
114
- #: bj-lazy-load.php:451
115
- msgid ""
116
- "string or number determining how long the animation will run - Default: 400"
117
- msgstr ""
118
-
119
- #: bj-lazy-load.php:456
120
- msgid ""
121
- "An offset of \"500\" would cause any images that are less than 500px below "
122
- "the bottom of the window or 500px above the top of the window to load. - "
123
- "Default: 200"
124
- msgstr ""
125
-
126
- #: bj-lazy-load.php:461
127
- msgid "You do not have sufficient permissions to access this page."
128
- msgstr ""
129
-
130
- #: bj-lazy-load.php:471
131
- msgid "Save Changes"
132
- msgstr ""
133
-
134
- #. Plugin Name of the plugin/theme
135
- msgid "BJ Lazy Load"
136
- msgstr ""
137
-
138
- #. Plugin URI of the plugin/theme
139
- msgid "http://wordpress.org/extend/plugins/bj-lazy-load/"
140
- msgstr ""
141
-
142
- #. Description of the plugin/theme
143
- msgid "Lazy image loading makes your site load faster and saves bandwidth."
144
- msgstr ""
145
-
146
- #. Author of the plugin/theme
147
- msgid "Bjørn Johansen"
148
- msgstr ""
149
-
150
- #. Author URI of the plugin/theme
151
- msgid "http://twitter.com/bjornjohansen"
152
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,24 +1,22 @@
1
  === BJ Lazy Load ===
2
  Contributors: bjornjohansen
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NLUWR4SHCJRBJ
4
- Tags: images, lazy loading, jquery, javascript, optimize, performance, bandwidth
5
  Author URI: http://twitter.com/bjornjohansen
6
- Requires at least: 3.1
7
- Tested up to: 3.3.2
8
- Stable tag: 0.4.0
9
 
10
- Lazy image loading makes your site load faster and saves bandwidth. Uses jQuery and degrades gracefully for non-js users.
11
 
12
  == Description ==
13
- Lazy image loading makes your site load faster and saves bandwidth.
14
 
15
- This plugin replaces all your post images and post thumbnails with a placeholder and loads images as they enter the browser window when the visitor scrolls the page.
16
 
17
- You can also lazy load other images in your theme, by using a simple function.
18
 
19
- Non-javascript visitors gets the original img element in noscript.
20
-
21
- Includes [JqueryAsynchImageLoader Plugin for jQuery by Sebastiano Armeli-Battana](http://www.sebastianoarmelibattana.com/projects/jail) for the lazy loading magic.
22
 
23
  = Coming soon =
24
  * Serving size optimized images for responsive design/adaptive layout
@@ -26,7 +24,7 @@ Includes [JqueryAsynchImageLoader Plugin for jQuery by Sebastiano Armeli-Battana
26
 
27
  == Installation ==
28
  1. Download and unzip plugin
29
- 2. Upload the 'bj-lazyload' folder to the '/wp-content/plugins/' directory,
30
  3. Activate the plugin through the 'Plugins' menu in WordPress.
31
 
32
  == Optional usage ==
@@ -42,7 +40,7 @@ echo $img;
42
  == Frequently Asked Questions ==
43
 
44
  = Whoa, this plugin is using JavaScript. What about visitors without JS? =
45
- No worries. They get the original image in a noscript element. No Lazy Loading for them, though.
46
 
47
  = Which browsers are supported? =
48
  The included JavaScript is tested in Firefox 2+, Safari 3+, Opera 9+, Chrome 5+, Internet Explorer 6+
@@ -58,6 +56,12 @@ Check your HTML source or see the magic at work in Web Inspector, FireBug or sim
58
 
59
  == Changelog ==
60
 
 
 
 
 
 
 
61
  = Version 0.4.0 =
62
  * Upgraded JAIL to version 0.9.9, fixing some bugs. Note: data-href is now renamed data-src.
63
 
@@ -108,6 +112,9 @@ Check your HTML source or see the magic at work in Web Inspector, FireBug or sim
108
 
109
  == Upgrade Notice ==
110
 
 
 
 
111
  = 0.4.0 =
112
  New JAIL version.
113
 
1
  === BJ Lazy Load ===
2
  Contributors: bjornjohansen
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NLUWR4SHCJRBJ
4
+ Tags: images, iframes, lazy loading, jquery, javascript, optimize, performance, bandwidth
5
  Author URI: http://twitter.com/bjornjohansen
6
+ Requires at least: 3.3
7
+ Tested up to: 3.4.1
8
+ Stable tag: 0.5.0
9
 
10
+ Lazy loading makes your site load faster and saves bandwidth. Uses jQuery and degrades gracefully for non-js users. Works with both images and iframes.
11
 
12
  == Description ==
13
+ Lazy loading makes your site load faster and saves bandwidth.
14
 
15
+ This plugin replaces all your post images, post thumbnails, gravatar images and content iframes with a placeholder and loads the content as it gets close to enter the browser window when the visitor scrolls the page.
16
 
17
+ You can also lazy load other images and iframes in your theme, by using a simple function.
18
 
19
+ Non-javascript visitors gets the original element in noscript.
 
 
20
 
21
  = Coming soon =
22
  * Serving size optimized images for responsive design/adaptive layout
24
 
25
  == Installation ==
26
  1. Download and unzip plugin
27
+ 2. Upload the 'bj-lazy-load' folder to the '/wp-content/plugins/' directory,
28
  3. Activate the plugin through the 'Plugins' menu in WordPress.
29
 
30
  == Optional usage ==
40
  == Frequently Asked Questions ==
41
 
42
  = Whoa, this plugin is using JavaScript. What about visitors without JS? =
43
+ No worries. They get the original element in a noscript element. No Lazy Loading for them, though.
44
 
45
  = Which browsers are supported? =
46
  The included JavaScript is tested in Firefox 2+, Safari 3+, Opera 9+, Chrome 5+, Internet Explorer 6+
56
 
57
  == Changelog ==
58
 
59
+ = Version 0.5.0 =
60
+ * Complete rewrite
61
+ * Replaced JAIL with jQuery.sonar to accomodate for iframe lazy loading
62
+ * Added lazy loading for iframes
63
+ * The manual filter code now works as it should, lazy loading all images instead of just the first.
64
+
65
  = Version 0.4.0 =
66
  * Upgraded JAIL to version 0.9.9, fixing some bugs. Note: data-href is now renamed data-src.
67
 
112
 
113
  == Upgrade Notice ==
114
 
115
+ = 0.5.0 =
116
+ Lazy load images and iframes. Complete rewrite.
117
+
118
  = 0.4.0 =
119
  New JAIL version.
120
 
scb/AdminPage.php ADDED
@@ -0,0 +1,453 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Administration page base class
4
+
5
+ abstract class scbAdminPage {
6
+ /** Page args
7
+ * $page_title string (mandatory)
8
+ * $parent (string) (default: options-general.php)
9
+ * $capability (string) (default: 'manage_options')
10
+ * $menu_title (string) (default: $page_title)
11
+ * $page_slug (string) (default: sanitized $page_title)
12
+ * $toplevel (string) If not empty, will create a new top level menu (for expected values see http://codex.wordpress.org/Administration_Menus#Using_add_submenu_page)
13
+ * - $icon_url (string) URL to an icon for the top level menu
14
+ * - $position (int) Position of the toplevel menu (caution!)
15
+ * $screen_icon (string) The icon type to use in the screen header
16
+ * $nonce string (default: $page_slug)
17
+ * $action_link (string|bool) Text of the action link on the Plugins page (default: 'Settings')
18
+ * $admin_action_priority int The priority that the admin_menu action should be executed at (default: 10)
19
+ */
20
+ protected $args;
21
+
22
+ // URL to the current plugin directory.
23
+ // Useful for adding css and js files
24
+ protected $plugin_url;
25
+
26
+ // Created at page init
27
+ protected $pagehook;
28
+
29
+ // scbOptions object holder
30
+ // Normally, it's used for storing formdata
31
+ protected $options;
32
+ protected $option_name;
33
+
34
+ // l10n
35
+ protected $textdomain;
36
+
37
+
38
+ // ____________REGISTRATION COMPONENT____________
39
+
40
+
41
+ private static $registered = array();
42
+
43
+ static function register( $class, $file, $options = null ) {
44
+ if ( isset( self::$registered[$class] ) )
45
+ return false;
46
+
47
+ self::$registered[$class] = array( $file, $options );
48
+
49
+ add_action( '_admin_menu', array( __CLASS__, '_pages_init' ) );
50
+
51
+ return true;
52
+ }
53
+
54
+ static function replace( $old_class, $new_class ) {
55
+ if ( ! isset( self::$registered[$old_class] ) )
56
+ return false;
57
+
58
+ self::$registered[$new_class] = self::$registered[$old_class];
59
+ unset( self::$registered[$old_class] );
60
+
61
+ return true;
62
+ }
63
+
64
+ static function remove( $class ) {
65
+ if ( ! isset( self::$registered[$class] ) )
66
+ return false;
67
+
68
+ unset( self::$registered[$class] );
69
+
70
+ return true;
71
+ }
72
+
73
+ static function _pages_init() {
74
+ foreach ( self::$registered as $class => $args )
75
+ new $class( $args[0], $args[1] );
76
+ }
77
+
78
+
79
+ // ____________MAIN METHODS____________
80
+
81
+
82
+ // Constructor
83
+ function __construct( $file = false, $options = null ) {
84
+ if ( is_a( $options, 'scbOptions' ) )
85
+ $this->options = $options;
86
+
87
+ $this->setup();
88
+ $this->check_args();
89
+
90
+ if ( isset( $this->option_name ) ) {
91
+ add_action( 'admin_init', array( $this, 'option_init' ) );
92
+ if ( function_exists( 'settings_errors' ) )
93
+ add_action( 'admin_notices', 'settings_errors' );
94
+ }
95
+
96
+ add_action( 'admin_menu', array( $this, 'page_init' ), $this->args['admin_action_priority'] );
97
+ add_filter( 'contextual_help', array( $this, '_contextual_help' ), 10, 2 );
98
+
99
+ if ( $file ) {
100
+ $this->file = $file;
101
+ $this->plugin_url = plugin_dir_url( $file );
102
+
103
+ if ( $this->args['action_link'] )
104
+ add_filter( 'plugin_action_links_' . plugin_basename( $file ), array( $this, '_action_link' ) );
105
+ }
106
+ }
107
+
108
+ // This is where all the page args can be set
109
+ function setup(){}
110
+
111
+ /**
112
+ * Called when the page is loaded, but before any rendering.
113
+ *
114
+ * Useful for calling $screen->add_help_tab() etc.
115
+ */
116
+ function page_loaded() {}
117
+
118
+ // This is where the css and js go
119
+ // Both wp_enqueue_*() and inline code can be added
120
+ function page_head(){}
121
+
122
+ // This is where the contextual help goes
123
+ // @return string
124
+ function page_help(){}
125
+
126
+ // A generic page header
127
+ function page_header() {
128
+ echo "<div class='wrap'>\n";
129
+ screen_icon( $this->args['screen_icon'] );
130
+ echo "<h2>" . $this->args['page_title'] . "</h2>\n";
131
+ }
132
+
133
+ // This is where the page content goes
134
+ abstract function page_content();
135
+
136
+ // A generic page footer
137
+ function page_footer() {
138
+ echo "</div>\n";
139
+ }
140
+
141
+ // This is where the form data should be validated
142
+ function validate( $new_data, $old_data ) {
143
+ return $new_data;
144
+ }
145
+
146
+ // Manually handle option saving ( use Settings API instead )
147
+ function form_handler() {
148
+ if ( empty( $_POST['action'] ) )
149
+ return false;
150
+
151
+ check_admin_referer( $this->nonce );
152
+
153
+ if ( !isset($this->options) ) {
154
+ trigger_error('options handler not set', E_USER_WARNING);
155
+ return false;
156
+ }
157
+
158
+ $new_data = wp_array_slice_assoc( $_POST, array_keys( $this->options->get_defaults() ) );
159
+
160
+ $new_data = stripslashes_deep( $new_data );
161
+
162
+ $new_data = $this->validate( $new_data, $this->options->get() );
163
+
164
+ $this->options->set( $new_data );
165
+
166
+ $this->admin_msg();
167
+ }
168
+
169
+ // Manually generate a standard admin notice ( use Settings API instead )
170
+ function admin_msg( $msg = '', $class = "updated" ) {
171
+ if ( empty( $msg ) )
172
+ $msg = __( 'Settings <strong>saved</strong>.', $this->textdomain );
173
+
174
+ echo scb_admin_notice( $msg, $class );
175
+ }
176
+
177
+
178
+ // ____________UTILITIES____________
179
+
180
+
181
+ // Generates a form submit button
182
+ function submit_button( $value = '', $action = 'action', $class = "button" ) {
183
+ if ( is_array( $value ) ) {
184
+ extract( wp_parse_args( $value, array(
185
+ 'value' => __( 'Save Changes', $this->textdomain ),
186
+ 'action' => 'action',
187
+ 'class' => 'button',
188
+ 'ajax' => true
189
+ ) ) );
190
+
191
+ if ( ! $ajax )
192
+ $class .= ' no-ajax';
193
+ }
194
+ else {
195
+ if ( empty( $value ) )
196
+ $value = __( 'Save Changes', $this->textdomain );
197
+ }
198
+
199
+ $input_args = array(
200
+ 'type' => 'submit',
201
+ 'name' => $action,
202
+ 'value' => $value,
203
+ 'extra' => '',
204
+ 'desc' => false,
205
+ 'wrap' => html( 'p class="submit"', scbForms::TOKEN )
206
+ );
207
+
208
+ if ( ! empty( $class ) )
209
+ $input_args['extra'] = compact( 'class' );
210
+
211
+ return scbForms::input( $input_args );
212
+ }
213
+
214
+ /*
215
+ Mimics scbForms::form_wrap()
216
+
217
+ $this->form_wrap( $content ); // generates a form with a default submit button
218
+
219
+ $this->form_wrap( $content, false ); // generates a form with no submit button
220
+
221
+ // the second argument is sent to submit_button()
222
+ $this->form_wrap( $content, array( 'text' => 'Save changes',
223
+ 'name' => 'action',
224
+ 'ajax' => true,
225
+ ) );
226
+ */
227
+ function form_wrap( $content, $submit_button = true ) {
228
+ if ( is_array( $submit_button ) ) {
229
+ $content .= $this->submit_button( $submit_button );
230
+ } elseif ( true === $submit_button ) {
231
+ $content .= $this->submit_button();
232
+ } elseif ( false !== strpos( $submit_button, '<input' ) ) {
233
+ $content .= $submit_button;
234
+ } elseif ( false !== $submit_button ) {
235
+ $button_args = array_slice( func_get_args(), 1 );
236
+ $content .= call_user_func_array( array( $this, 'submit_button' ), $button_args );
237
+ }
238
+
239
+ return scbForms::form_wrap( $content, $this->nonce );
240
+ }
241
+
242
+ // Generates a table wrapped in a form
243
+ function form_table( $rows, $formdata = false ) {
244
+ $output = '';
245
+ foreach ( $rows as $row )
246
+ $output .= $this->table_row( $row, $formdata );
247
+
248
+ $output = $this->form_table_wrap( $output );
249
+
250
+ return $output;
251
+ }
252
+
253
+ // Wraps the given content in a <form><table>
254
+ function form_table_wrap( $content ) {
255
+ $output = $this->table_wrap( $content );
256
+ $output = $this->form_wrap( $output );
257
+
258
+ return $output;
259
+ }
260
+
261
+ // Generates a form table
262
+ function table( $rows, $formdata = false ) {
263
+ $output = '';
264
+ foreach ( $rows as $row )
265
+ $output .= $this->table_row( $row, $formdata );
266
+
267
+ $output = $this->table_wrap( $output );
268
+
269
+ return $output;
270
+ }
271
+
272
+ // Generates a table row
273
+ function table_row( $args, $formdata = false ) {
274
+ return $this->row_wrap( $args['title'], $this->input( $args, $formdata ) );
275
+ }
276
+
277
+ // Wraps the given content in a <table>
278
+ function table_wrap( $content ) {
279
+ return
280
+ html( 'table class="form-table"', $content );
281
+ }
282
+
283
+ // Wraps the given content in a <tr><td>
284
+ function row_wrap( $title, $content ) {
285
+ return
286
+ html( 'tr',
287
+ html( 'th scope="row"', $title )
288
+ .html( 'td', $content ) );
289
+ }
290
+
291
+ // Mimic scbForms inheritance
292
+ function __call( $method, $args ) {
293
+ if ( in_array( $method, array( 'input', 'form' ) ) ) {
294
+ if ( empty( $args[1] ) && isset( $this->options ) )
295
+ $args[1] = $this->options->get();
296
+
297
+ if ( 'form' == $method )
298
+ $args[2] = $this->nonce;
299
+ }
300
+
301
+ return call_user_func_array( array( 'scbForms', $method ), $args );
302
+ }
303
+
304
+ // Wraps a string in a <script> tag
305
+ function js_wrap( $string ) {
306
+ return "\n<script type='text/javascript'>\n" . $string . "\n</script>\n";
307
+ }
308
+
309
+ // Wraps a string in a <style> tag
310
+ function css_wrap( $string ) {
311
+ return "\n<style type='text/css'>\n" . $string . "\n</style>\n";
312
+ }
313
+
314
+
315
+ // ____________INTERNAL METHODS____________
316
+
317
+
318
+ // Registers a page
319
+ function page_init() {
320
+ extract( $this->args );
321
+
322
+ if ( ! $toplevel ) {
323
+ $this->pagehook = add_submenu_page( $parent, $page_title, $menu_title, $capability, $page_slug, array( $this, '_page_content_hook' ) );
324
+ } else {
325
+ $func = 'add_' . $toplevel . '_page';
326
+ $this->pagehook = $func( $page_title, $menu_title, $capability, $page_slug, array( $this, '_page_content_hook' ), $icon_url, $position );
327
+ }
328
+
329
+ if ( ! $this->pagehook )
330
+ return;
331
+
332
+ add_action( 'load-' . $this->pagehook, array( $this, 'page_loaded' ) );
333
+
334
+ if ( $ajax_submit ) {
335
+ $this->ajax_response();
336
+ add_action( 'admin_footer', array( $this, 'ajax_submit' ), 20 );
337
+ }
338
+
339
+ add_action( 'admin_print_styles-' . $this->pagehook, array( $this, 'page_head' ) );
340
+ }
341
+
342
+ function option_init() {
343
+ register_setting( $this->option_name, $this->option_name, array( $this, 'validate' ) );
344
+ }
345
+
346
+ private function check_args() {
347
+ if ( empty( $this->args['page_title'] ) )
348
+ trigger_error( 'Page title cannot be empty', E_USER_WARNING );
349
+
350
+ $this->args = wp_parse_args( $this->args, array(
351
+ 'toplevel' => '',
352
+ 'position' => null,
353
+ 'icon_url' => '',
354
+ 'screen_icon' => '',
355
+ 'parent' => 'options-general.php',
356
+ 'capability' => 'manage_options',
357
+ 'menu_title' => $this->args['page_title'],
358
+ 'page_slug' => '',
359
+ 'nonce' => '',
360
+ 'action_link' => __( 'Settings', $this->textdomain ),
361
+ 'ajax_submit' => false,
362
+ 'admin_action_priority' => 10,
363
+ ) );
364
+
365
+ if ( empty( $this->args['page_slug'] ) )
366
+ $this->args['page_slug'] = sanitize_title_with_dashes( $this->args['menu_title'] );
367
+
368
+ if ( empty( $this->args['nonce'] ) )
369
+ $this->nonce = $this->args['page_slug'];
370
+ }
371
+
372
+ function _contextual_help( $help, $screen ) {
373
+ if ( is_object( $screen ) )
374
+ $screen = $screen->id;
375
+
376
+ $actual_help = $this->page_help();
377
+
378
+ if ( $screen == $this->pagehook && $actual_help )
379
+ return $actual_help;
380
+
381
+ return $help;
382
+ }
383
+
384
+ function ajax_response() {
385
+ if ( ! isset( $_POST['_ajax_submit'] ) || $_POST['_ajax_submit'] != $this->pagehook )
386
+ return;
387
+
388
+ $this->form_handler();
389
+ die;
390
+ }
391
+
392
+ function ajax_submit() {
393
+ global $page_hook;
394
+
395
+ if ( $page_hook != $this->pagehook )
396
+ return;
397
+ ?>
398
+ <script type="text/javascript">
399
+ jQuery( document ).ready( function( $ ){
400
+ var $spinner = $( new Image() ).attr( 'src', '<?php echo admin_url( "images/wpspin_light.gif" ); ?>' );
401
+
402
+ $( ':submit' ).click( function( ev ){
403
+ var $submit = $( this );
404
+ var $form = $submit.parents( 'form' );
405
+
406
+ if ( $submit.hasClass( 'no-ajax' ) || $form.attr( 'method' ).toLowerCase() != 'post' )
407
+ return true;
408
+
409
+ var $this_spinner = $spinner.clone();
410
+
411
+ $submit.before( $this_spinner ).hide();
412
+
413
+ var data = $form.serializeArray();
414
+ data.push( {name: $submit.attr( 'name' ), value: $submit.val()} );
415
+ data.push( {name: '_ajax_submit', value: '<?php echo $this->pagehook; ?>'} );
416
+
417
+ $.post( location.href, data, function( response ){
418
+ var $prev = $( '.wrap > .updated, .wrap > .error' );
419
+ var $msg = $( response ).hide().insertAfter( $( '.wrap h2' ) );
420
+ if ( $prev.length > 0 )
421
+ $prev.fadeOut( 'slow', function(){ $msg.fadeIn( 'slow' ); } );
422
+ else
423
+ $msg.fadeIn( 'slow' );
424
+
425
+ $this_spinner.hide();
426
+ $submit.show();
427
+ } );
428
+
429
+ ev.stopPropagation();
430
+ ev.preventDefault();
431
+ } );
432
+ } );
433
+ </script>
434
+ <?php
435
+ }
436
+
437
+ function _page_content_hook() {
438
+ $this->form_handler();
439
+
440
+ $this->page_header();
441
+ $this->page_content();
442
+ $this->page_footer();
443
+ }
444
+
445
+ function _action_link( $links ) {
446
+ $url = add_query_arg( 'page', $this->args['page_slug'], admin_url( $this->args['parent'] ) );
447
+
448
+ $links[] = html_link( $url, $this->args['action_link'] );
449
+
450
+ return $links;
451
+ }
452
+ }
453
+
scb/BoxesPage.php ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Admin screen with metaboxes base class
4
+
5
+ abstract class scbBoxesPage extends scbAdminPage {
6
+ /*
7
+ A box definition looks like this:
8
+ array( $slug, $title, $column );
9
+
10
+ Available columns: normal, side, column3, column4
11
+ */
12
+ protected $boxes = array();
13
+
14
+ function __construct( $file = false, $options = null ) {
15
+ parent::__construct( $file, $options );
16
+
17
+ scbUtil::add_uninstall_hook( $this->file, array( $this, 'uninstall' ) );
18
+ }
19
+
20
+ function page_init() {
21
+ if ( !isset( $this->args['columns'] ) )
22
+ $this->args['columns'] = 2;
23
+
24
+ parent::page_init();
25
+
26
+ add_action( 'load-' . $this->pagehook, array( $this, 'boxes_init' ) );
27
+ }
28
+
29
+ function default_css() {
30
+ ?>
31
+ <style type="text/css">
32
+ .postbox-container + .postbox-container {
33
+ margin-left: 18px;
34
+ }
35
+ .postbox-container {
36
+ padding-right: 0;
37
+ }
38
+ .inside {
39
+ clear: both;
40
+ overflow: hidden;
41
+ }
42
+ .inside table {
43
+ margin: 0 !important;
44
+ padding: 0 !important;
45
+ }
46
+ .inside table td {
47
+ vertical-align: middle !important;
48
+ }
49
+ .inside table .regular-text {
50
+ width: 100% !important;
51
+ }
52
+ .inside .form-table th {
53
+ width: 30%;
54
+ max-width: 200px;
55
+ padding: 10px 0 !important;
56
+ }
57
+ .inside .widefat .check-column {
58
+ padding-bottom: 7px !important;
59
+ }
60
+ .inside p,
61
+ .inside table {
62
+ margin: 0 0 10px !important;
63
+ }
64
+ .inside p.submit {
65
+ float: left !important;
66
+ padding: 0 !important;
67
+ margin-bottom: 0 !important;
68
+ }
69
+ </style>
70
+ <?php
71
+ }
72
+
73
+ function page_content() {
74
+ $this->default_css();
75
+
76
+ global $screen_layout_columns;
77
+
78
+ if ( isset( $screen_layout_columns ) ) {
79
+ $hide2 = $hide3 = $hide4 = '';
80
+ switch ( $screen_layout_columns ) {
81
+ case 4:
82
+ if( !isset( $this->args['column_widths'] ) )
83
+ $this->args['column_widths'] = array( 24.5, 24.5, 24.5, 24.5 );
84
+ break;
85
+ case 3:
86
+ if( !isset( $this->args['column_widths'] ) )
87
+ $this->args['column_widths'] = array( 32.67, 32.67, 32.67 );
88
+ $hide4 = 'display:none;';
89
+ break;
90
+ case 2:
91
+ if( !isset( $this->args['column_widths'] ) )
92
+ $this->args['column_widths'] = array( 49, 49 );
93
+ $hide3 = $hide4 = 'display:none;';
94
+ break;
95
+ default:
96
+ if( !isset( $this->args['column_widths'] ) )
97
+ $this->args['column_widths'] = array( 98 );
98
+ $hide2 = $hide3 = $hide4 = 'display:none;';
99
+ }
100
+
101
+ $this->args['column_widths'] = array_pad( $this->args['column_widths'], 4, 0 );
102
+ }
103
+ ?>
104
+ <div id='<?php echo $this->pagehook ?>-widgets' class='metabox-holder'>
105
+ <?php
106
+ echo "\t<div class='postbox-container' style='width:{$this->args['column_widths'][0]}%'>\n";
107
+ do_meta_boxes( $this->pagehook, 'normal', '' );
108
+
109
+ echo "\t</div><div class='postbox-container' style='width:{$hide2}{$this->args['column_widths'][1]}%'>\n";
110
+ do_meta_boxes( $this->pagehook, 'side', '' );
111
+
112
+ echo "\t</div><div class='postbox-container' style='width:{$hide3}{$this->args['column_widths'][2]}%'>\n";
113
+ do_meta_boxes( $this->pagehook, 'column3', '' );
114
+
115
+ echo "\t</div><div class='postbox-container' style='width:{$hide4}{$this->args['column_widths'][3]}%'>\n";
116
+ do_meta_boxes( $this->pagehook, 'column4', '' );
117
+ ?>
118
+ </div></div>
119
+ <?php
120
+ }
121
+
122
+ function page_footer() {
123
+ parent::page_footer();
124
+ $this->_boxes_js_init();
125
+ }
126
+
127
+ function form_handler() {
128
+ if ( empty( $_POST ) )
129
+ return;
130
+
131
+ check_admin_referer( $this->nonce );
132
+
133
+ // Box handler
134
+ foreach ( $this->boxes as $box ) {
135
+ $args = isset( $box[4] ) ? $box[4] : array();
136
+
137
+ $handler = $box[0] . '_handler';
138
+
139
+ if ( method_exists( $this, $handler ) )
140
+ call_user_func_array( array( $this, $handler ), $args );
141
+ }
142
+ }
143
+
144
+ function uninstall() {
145
+ global $wpdb;
146
+
147
+ $hook = str_replace( '-', '', $this->pagehook );
148
+
149
+ foreach ( array( 'metaboxhidden', 'closedpostboxes', 'wp_metaboxorder', 'screen_layout' ) as $option )
150
+ $keys[] = "'{$option}_{$hook}'";
151
+
152
+ $keys = '( ' . implode( ', ', $keys ) . ' )';
153
+
154
+ $wpdb->query( "
155
+ DELETE FROM {$wpdb->usermeta}
156
+ WHERE meta_key IN {$keys}
157
+ " );
158
+ }
159
+
160
+ function boxes_init() {
161
+ wp_enqueue_script( 'postbox' );
162
+
163
+ add_screen_option( 'layout_columns', array(
164
+ 'max' => $this->args['columns'],
165
+ 'default' => $this->args['columns']
166
+ ) );
167
+
168
+ $registered = array();
169
+ foreach ( $this->boxes as $box_args ) {
170
+ foreach ( array( 'name', 'title', 'context', 'priority', 'args' ) as $i => $arg ) {
171
+ if ( isset( $box_args[$i] ) )
172
+ $$arg = $box_args[$i];
173
+ }
174
+
175
+ if ( empty( $title ) )
176
+ $title = ucfirst( $name );
177
+ if ( empty( $context ) )
178
+ $context = 'normal';
179
+ if ( empty( $priority ) )
180
+ $priority = 'default';
181
+ if ( empty( $args ) )
182
+ $args = array();
183
+
184
+ if ( isset( $registered[$name] ) ) {
185
+ if ( empty( $args ) )
186
+ trigger_error( "Duplicate box name: $name", E_USER_NOTICE );
187
+
188
+ $name = $this->_increment( $name );
189
+ } else {
190
+ $registered[$name] = true;
191
+ }
192
+
193
+ add_meta_box( $name, $title, array( $this, '_intermediate_callback' ), $this->pagehook, $context, $priority, $args );
194
+ }
195
+ }
196
+
197
+ // Make it so that $args is actually what's passed to the callback
198
+ function _intermediate_callback( $_, $box ) {
199
+ list( $name ) = explode( '-', $box['id'] );
200
+
201
+ call_user_func_array( array( $this, $name . '_box' ), $box['args'] );
202
+ }
203
+
204
+ private function _increment( $name ) {
205
+ $parts = explode( '-', $name );
206
+ if ( isset( $parts[1] ) )
207
+ $parts[1]++;
208
+ else
209
+ $parts[1] = 2;
210
+
211
+ return implode( '-', $parts );
212
+ }
213
+
214
+ // Adds necesary code for JS to work
215
+ function _boxes_js_init() {
216
+ echo $this->js_wrap( <<<EOT
217
+ jQuery( document ).ready( function( $ ){
218
+ // close postboxes that should be closed
219
+ $( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
220
+ // postboxes setup
221
+ postboxes.add_postbox_toggles( '$this->pagehook' );
222
+ } );
223
+ EOT
224
+ );
225
+ ?>
226
+
227
+ <form style='display: none' method='get' action=''>
228
+ <p>
229
+ <?php
230
+ wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
231
+ wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
232
+ ?>
233
+ </p>
234
+ </form>
235
+ <?php
236
+ }
237
+ }
238
+
239
+
240
+
scb/Cron.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // wp-cron job container
4
+
5
+ class scbCron {
6
+ protected $schedule;
7
+ protected $interval;
8
+ protected $time;
9
+
10
+ protected $hook;
11
+ protected $callback_args = array();
12
+
13
+ /**
14
+ * Create a new cron job
15
+ *
16
+ * @param string Reference to main plugin file
17
+ * @param array List of args:
18
+ string $action OR callback $callback
19
+ string $schedule OR number $interval
20
+ array $callback_args (optional)
21
+ */
22
+ function __construct( $file = false, $args ) {
23
+ extract( $args, EXTR_SKIP );
24
+
25
+ // Set time & schedule
26
+ if ( isset( $time ) )
27
+ $this->time = $time;
28
+
29
+ if ( isset( $interval ) ) {
30
+ $this->schedule = $interval . 'secs';
31
+ $this->interval = $interval;
32
+ } elseif ( isset( $schedule ) ) {
33
+ $this->schedule = $schedule;
34
+ }
35
+
36
+ // Set hook
37
+ if ( isset( $action ) ) {
38
+ $this->hook = $action;
39
+ } elseif ( isset( $callback ) ) {
40
+ $this->hook = self::_callback_to_string( $callback );
41
+ add_action( $this->hook, $callback );
42
+ } elseif ( method_exists( $this, 'callback' ) ) {
43
+ $this->hook = self::_callback_to_string( array( $this, 'callback' ) );
44
+ add_action( $this->hook, $callback );
45
+ } else {
46
+ trigger_error( '$action OR $callback not set', E_USER_WARNING );
47
+ }
48
+
49
+ if ( isset( $callback_args ) )
50
+ $this->callback_args = (array) $callback_args;
51
+
52
+ if ( $file && $this->schedule ) {
53
+ scbUtil::add_activation_hook( $file, array( $this, 'reset' ) );
54
+ register_deactivation_hook( $file, array( $this, 'unschedule' ) );
55
+ }
56
+
57
+ add_filter( 'cron_schedules', array( $this, '_add_timing' ) );
58
+ }
59
+
60
+ /* Change the interval of the cron job
61
+ *
62
+ * @param array List of args:
63
+ string $schedule OR number $interval
64
+ timestamp $time ( optional )
65
+ */
66
+ function reschedule( $args ) {
67
+ extract( $args );
68
+
69
+ if ( $schedule && $this->schedule != $schedule ) {
70
+ $this->schedule = $schedule;
71
+ } elseif ( $interval && $this->interval != $interval ) {
72
+ $this->schedule = $interval . 'secs';
73
+ $this->interval = $interval;
74
+ }
75
+
76
+ $this->time = $time;
77
+
78
+ $this->reset();
79
+ }
80
+
81
+ /**
82
+ * Reset the schedule
83
+ */
84
+ function reset() {
85
+ $this->unschedule();
86
+ $this->schedule();
87
+ }
88
+
89
+ /**
90
+ * Clear the cron job
91
+ */
92
+ function unschedule() {
93
+ # wp_clear_scheduled_hook( $this->hook, $this->callback_args );
94
+ self::really_clear_scheduled_hook( $this->hook );
95
+ }
96
+
97
+ /**
98
+ * Execute the job now
99
+ * @param array $args List of arguments to pass to the callback
100
+ */
101
+ function do_now( $args = null ) {
102
+ if ( is_null( $args ) )
103
+ $args = $this->callback_args;
104
+
105
+ do_action_ref_array( $this->hook, $args );
106
+ }
107
+
108
+ /**
109
+ * Execute the job with a given delay
110
+ * @param int $delay in seconds
111
+ * @param array $args List of arguments to pass to the callback
112
+ */
113
+ function do_once( $delay = 0, $args = null ) {
114
+ if ( is_null( $args ) )
115
+ $args = $this->callback_args;
116
+
117
+ wp_clear_scheduled_hook( $this->hook, $args );
118
+ wp_schedule_single_event( time() + $delay, $this->hook, $args );
119
+ }
120
+
121
+
122
+ //_____INTERNAL METHODS_____
123
+
124
+
125
+ function _add_timing( $schedules ) {
126
+ if ( isset( $schedules[$this->schedule] ) )
127
+ return $schedules;
128
+
129
+ $schedules[$this->schedule] = array( 'interval' => $this->interval,
130
+ 'display' => $this->interval . ' seconds' );
131
+
132
+ return $schedules;
133
+ }
134
+
135
+ protected function schedule() {
136
+ if ( ! $this->time )
137
+ $this->time = time();
138
+
139
+ wp_schedule_event( $this->time, $this->schedule, $this->hook, $this->callback_args );
140
+ }
141
+
142
+ protected static function really_clear_scheduled_hook( $name ) {
143
+ $crons = _get_cron_array();
144
+
145
+ foreach ( $crons as $timestamp => $hooks ) {
146
+ foreach ( $hooks as $hook => $args )
147
+ if ( $hook == $name )
148
+ unset( $crons[$timestamp][$hook] );
149
+
150
+ if ( empty( $hooks ) )
151
+ unset( $crons[$timestamp] );
152
+ }
153
+
154
+ _set_cron_array( $crons );
155
+ }
156
+
157
+ protected static function _callback_to_string( $callback ) {
158
+ if ( ! is_array( $callback ) )
159
+ $str = $callback;
160
+ elseif ( ! is_string( $callback[0] ) )
161
+ $str = get_class( $callback[0] ) . '_' . $callback[1];
162
+ else
163
+ $str = $callback[0] . '::' . $callback[1];
164
+
165
+ $str .= '_hook';
166
+
167
+ return $str;
168
+ }
169
+ }
170
+
scb/Forms.php ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Data-aware form generator
4
+
5
+ class scbForms {
6
+
7
+ const TOKEN = '%input%';
8
+
9
+ protected static $cur_name;
10
+
11
+ function input_with_value( $args, $value ) {
12
+ if ( is_null( $value ) && isset( $args['default'] ) )
13
+ $value = $args['default'];
14
+
15
+ if ( !is_null( $value ) ) {
16
+ switch ( $args['type'] ) {
17
+ case 'select':
18
+ case 'radio':
19
+ $args['selected'] = $value;
20
+ break;
21
+ case 'checkbox':
22
+ if ( is_array( $value ) )
23
+ $args['checked'] = $value;
24
+ else
25
+ $args['checked'] = ( $value || ( isset( $args['value'] ) && $value == $args['value'] ) );
26
+ break;
27
+ default:
28
+ $args['value'] = $value;
29
+ }
30
+ }
31
+
32
+ return self::input( $args );
33
+ }
34
+
35
+ static function input( $args, $formdata = false ) {
36
+ if ( false !== $formdata ) {
37
+ $form = new scbForm( $formdata );
38
+ return $form->input( $args );
39
+ }
40
+
41
+ if ( empty( $args['name'] ) ) {
42
+ return trigger_error( 'Empty name', E_USER_WARNING );
43
+ }
44
+
45
+ $args = wp_parse_args( $args, array(
46
+ 'desc' => '',
47
+ 'desc_pos' => 'after',
48
+ 'wrap' => self::TOKEN,
49
+ 'wrap_each' => self::TOKEN,
50
+ ) );
51
+
52
+ if ( isset( $args['value'] ) && is_array( $args['value'] ) ) {
53
+ $args['values'] = $args['value'];
54
+ unset( $args['value'] );
55
+ }
56
+
57
+ if ( isset( $args['extra'] ) && !is_array( $args['extra'] ) )
58
+ $args['extra'] = shortcode_parse_atts( $args['extra'] );
59
+
60
+ self::$cur_name = self::get_name( $args['name'] );
61
+
62
+ switch ( $args['type'] ) {
63
+ case 'select':
64
+ case 'radio':
65
+ $input = self::_single_choice( $args );
66
+ break;
67
+ case 'checkbox':
68
+ if ( isset( $args['values'] ) )
69
+ $input = self::_multiple_choice( $args );
70
+ else
71
+ $input = self::_checkbox( $args );
72
+ break;
73
+ default:
74
+ $input = self::_input( $args );
75
+ }
76
+
77
+ return str_replace( self::TOKEN, $input, $args['wrap'] );
78
+ }
79
+
80
+
81
+ // ____________UTILITIES____________
82
+
83
+
84
+ // Generates a table wrapped in a form
85
+ static function form_table( $rows, $formdata = NULL ) {
86
+ $output = '';
87
+ foreach ( $rows as $row )
88
+ $output .= self::table_row( $row, $formdata );
89
+
90
+ $output = self::form_table_wrap( $output );
91
+
92
+ return $output;
93
+ }
94
+
95
+ // Generates a form
96
+ static function form( $inputs, $formdata = NULL, $nonce ) {
97
+ $output = '';
98
+ foreach ( $inputs as $input )
99
+ $output .= self::input( $input, $formdata );
100
+
101
+ $output = self::form_wrap( $output, $nonce );
102
+
103
+ return $output;
104
+ }
105
+
106
+ // Generates a table
107
+ static function table( $rows, $formdata = NULL ) {
108
+ $output = '';
109
+ foreach ( $rows as $row )
110
+ $output .= self::table_row( $row, $formdata );
111
+
112
+ $output = self::table_wrap( $output );
113
+
114
+ return $output;
115
+ }
116
+
117
+ // Generates a table row
118
+ static function table_row( $args, $formdata = NULL ) {
119
+ return self::row_wrap( $args['title'], self::input( $args, $formdata ) );
120
+ }
121
+
122
+
123
+ // ____________WRAPPERS____________
124
+
125
+
126
+ // Wraps the given content in a <form><table>
127
+ static function form_table_wrap( $content, $nonce = 'update_options' ) {
128
+ $output = self::table_wrap( $content );
129
+ $output = self::form_wrap( $output, $nonce );
130
+
131
+ return $output;
132
+ }
133
+
134
+ // Wraps the given content in a <form> tag
135
+ static function form_wrap( $content, $nonce = 'update_options' ) {
136
+ $output = "\n<form method='post' action=''>\n";
137
+ $output .= $content;
138
+ $output .= wp_nonce_field( $action = $nonce, $name = "_wpnonce", $referer = true , $echo = false );
139
+ $output .= "\n</form>\n";
140
+
141
+ return $output;
142
+ }
143
+
144
+ // Wraps the given content in a <table>
145
+ static function table_wrap( $content ) {
146
+ $output = "\n<table class='form-table'>\n" . $content . "\n</table>\n";
147
+
148
+ return $output;
149
+ }
150
+
151
+ // Wraps the given content in a <tr><td>
152
+ static function row_wrap( $title, $content ) {
153
+ return "\n<tr>\n\t<th scope='row'>" . $title . "</th>\n\t<td>\n\t\t" . $content . "\t</td>\n\n</tr>";
154
+ }
155
+
156
+
157
+ // ____________PRIVATE METHODS____________
158
+
159
+
160
+ private static function _single_choice( $args ) {
161
+ $args = wp_parse_args( $args, array(
162
+ 'numeric' => false, // use numeric array instead of associative
163
+ 'selected' => array( 'foo' ), // hack to make default blank
164
+ ) );
165
+
166
+ self::_expand_values( $args );
167
+
168
+ if ( 'select' == $args['type'] )
169
+ return self::_select( $args );
170
+ else
171
+ return self::_radio( $args );
172
+ }
173
+
174
+ private static function _multiple_choice( $args ) {
175
+ $args = wp_parse_args( $args, array(
176
+ 'numeric' => false, // use numeric array instead of associative
177
+ 'checked' => null,
178
+ ) );
179
+
180
+ self::$cur_name .= '[]';
181
+
182
+ self::_expand_values( $args );
183
+
184
+ extract( $args );
185
+
186
+ if ( !is_array( $checked ) )
187
+ $checked = array();
188
+
189
+ $opts = '';
190
+ foreach ( $values as $value => $title ) {
191
+ $single_input = self::_checkbox( array(
192
+ 'type' => 'checkbox',
193
+ 'value' => $value,
194
+ 'checked' => in_array( $value, $checked ),
195
+ 'desc' => $title,
196
+ 'desc_pos' => 'after'
197
+ ) );
198
+
199
+ $opts .= str_replace( self::TOKEN, $single_input, $args['wrap_each'] );
200
+ }
201
+
202
+ return self::add_desc( $opts, $desc, $desc_pos );
203
+ }
204
+
205
+ private static function _expand_values( &$args ) {
206
+ $values =& $args['values'];
207
+
208
+ if ( !empty( $values ) && !self::is_associative( $values ) ) {
209
+ if ( is_array( $args['desc'] ) ) {
210
+ $values = array_combine( $values, $args['desc'] ); // back-compat
211
+ $args['desc'] = false;
212
+ } elseif ( !isset( $args['numeric'] ) || !$args['numeric'] ) {
213
+ $values = array_combine( $values, $values );
214
+ }
215
+ }
216
+ }
217
+
218
+ private static function _radio( $args ) {
219
+ extract( $args );
220
+
221
+ if ( array( 'foo' ) == $selected ) {
222
+ // radio buttons should always have one option selected
223
+ $selected = key( $values );
224
+ }
225
+
226
+ $opts = '';
227
+ foreach ( $values as $value => $title ) {
228
+ $single_input = self::_checkbox( array(
229
+ 'type' => 'radio',
230
+ 'value' => $value,
231
+ 'checked' => ( (string) $value == (string) $selected ),
232
+ 'desc' => $title,
233
+ 'desc_pos' => 'after'
234
+ ) );
235
+
236
+ $opts .= str_replace( self::TOKEN, $single_input, $args['wrap_each'] );
237
+ }
238
+
239
+ return self::add_desc( $opts, $desc, $desc_pos );
240
+ }
241
+
242
+ private static function _select( $args ) {
243
+ extract( wp_parse_args( $args, array(
244
+ 'text' => false,
245
+ 'extra' => array()
246
+ ) ) );
247
+
248
+ $options = array();
249
+
250
+ if ( false !== $text ) {
251
+ $options[] = array(
252
+ 'value' => '',
253
+ 'selected' => ( $selected == array( 'foo' ) ),
254
+ 'title' => $text
255
+ );
256
+ }
257
+
258
+ foreach ( $values as $value => $title ) {
259
+ $options[] = array(
260
+ 'value' => $value,
261
+ 'selected' => ( (string) $value == (string) $selected ),
262
+ 'title' => $title
263
+ );
264
+ }
265
+
266
+ $opts = '';
267
+ foreach ( $options as $option ) {
268
+ extract( $option );
269
+
270
+ $opts .= html( 'option', compact( 'value', 'selected' ), $title );
271
+ }
272
+
273
+ $extra['name'] = self::$cur_name;
274
+
275
+ $input = html( 'select', $extra, $opts );
276
+
277
+ return self::add_label( $input, $desc, $desc_pos );
278
+ }
279
+
280
+ // Handle args for a single checkbox or radio input
281
+ private static function _checkbox( $args ) {
282
+ $args = wp_parse_args( $args, array(
283
+ 'value' => true,
284
+ 'desc' => NULL,
285
+ 'checked' => false,
286
+ 'extra' => array(),
287
+ ) );
288
+
289
+ foreach ( $args as $key => &$val )
290
+ $$key = &$val;
291
+ unset( $val );
292
+
293
+ $extra['checked'] = $checked;
294
+
295
+ if ( is_null( $desc ) && !is_bool( $value ) )
296
+ $desc = str_replace( '[]', '', $value );
297
+
298
+ return self::_input_gen( $args );
299
+ }
300
+
301
+ // Handle args for text inputs
302
+ private static function _input( $args ) {
303
+ $args = wp_parse_args( $args, array(
304
+ 'value' => '',
305
+ 'desc_pos' => 'after',
306
+ 'extra' => array( 'class' => 'regular-text' ),
307
+ ) );
308
+
309
+ foreach ( $args as $key => &$val )
310
+ $$key = &$val;
311
+ unset( $val );
312
+
313
+ if ( !isset( $extra['id'] ) && !is_array( $name ) && false === strpos( $name, '[' ) )
314
+ $extra['id'] = $name;
315
+
316
+ return self::_input_gen( $args );
317
+ }
318
+
319
+ // Generate html with the final args
320
+ private static function _input_gen( $args ) {
321
+ extract( wp_parse_args( $args, array(
322
+ 'value' => NULL,
323
+ 'desc' => NULL,
324
+ 'extra' => array()
325
+ ) ) );
326
+
327
+ $extra['name'] = self::$cur_name;
328
+
329
+ if ( 'textarea' == $type ) {
330
+ $input = html( 'textarea', $extra, esc_textarea( $value ) );
331
+ } else {
332
+ $extra['value'] = $value;
333
+ $extra['type'] = $type;
334
+ $input = html( 'input', $extra );
335
+ }
336
+
337
+ return self::add_label( $input, $desc, $desc_pos );
338
+ }
339
+
340
+ private static function add_label( $input, $desc, $desc_pos ) {
341
+ return html( 'label', self::add_desc( $input, $desc, $desc_pos ) ) . "\n";
342
+ }
343
+
344
+ private static function add_desc( $input, $desc, $desc_pos ) {
345
+ if ( empty( $desc ) )
346
+ return $input;
347
+
348
+ if ( 'before' == $desc_pos )
349
+ return $desc . ' ' . $input;
350
+ else
351
+ return $input . ' ' . $desc;
352
+ }
353
+
354
+
355
+ // Utilities
356
+
357
+
358
+ /**
359
+ * Generates the proper string for a name attribute.
360
+ *
361
+ * @param array|string $name The raw name
362
+ *
363
+ * @return string
364
+ */
365
+ static function get_name( $name ) {
366
+ $name = (array) $name;
367
+
368
+ $name_str = array_shift( $name );
369
+
370
+ foreach ( $name as $key ) {
371
+ $name_str .= '[' . esc_attr( $key ) . ']';
372
+ }
373
+
374
+ return $name_str;
375
+ }
376
+
377
+ /**
378
+ * Traverses the formdata and retrieves the correct value.
379
+ *
380
+ * @param array|string $name The name of the value
381
+ * @param array $value The data that will be traversed
382
+ * @param mixed $fallback The value returned when the key is not found
383
+ *
384
+ * @return mixed
385
+ */
386
+ static function get_value( $name, $value, $fallback = null ) {
387
+ foreach ( (array) $name as $key ) {
388
+ if ( !isset( $value[ $key ] ) )
389
+ return $fallback;
390
+
391
+ $value = $value[$key];
392
+ }
393
+
394
+ return $value;
395
+ }
396
+
397
+ /**
398
+ * Given a list of fields, extract the appropriate POST data and return it.
399
+ *
400
+ * @param array $fields List of args that would be sent to scbForms::input()
401
+ * @param array $to_update Existing data to update
402
+ *
403
+ * @return array
404
+ */
405
+ static function validate_post_data( $fields, $to_update = array() ) {
406
+ foreach ( $fields as $field ) {
407
+ $value = scbForms::get_value( $field['name'], $_POST );
408
+
409
+ $value = stripslashes_deep( $value );
410
+
411
+ switch ( $field['type'] ) {
412
+ case 'checkbox':
413
+ if ( isset( $field['values'] ) && is_array( $field['values'] ) )
414
+ $value = array_intersect( $field['values'], (array) $value );
415
+ else
416
+ $value = (bool) $value;
417
+
418
+ break;
419
+ case 'radio':
420
+ case 'select':
421
+ self::_expand_values( $field );
422
+
423
+ if ( !isset( $field['values'][ $value ] ) )
424
+ continue 2;
425
+ }
426
+
427
+ self::set_value( $to_update, $field['name'], $value );
428
+ }
429
+
430
+ return $to_update;
431
+ }
432
+
433
+ static function input_from_meta( $args, $object_id, $meta_type = 'post' ) {
434
+ $single = ( 'checkbox' != $args['type'] );
435
+
436
+ $key = (array) $args['name'];
437
+ $key = end( $key );
438
+
439
+ $value = get_metadata( $meta_type, $object_id, $key );
440
+
441
+ if ( empty( $value ) )
442
+ $value = null;
443
+ elseif( $single )
444
+ $value = reset( $value );
445
+
446
+ return self::input_with_value( $args, $value );
447
+ }
448
+
449
+ static function update_meta( $fields, $data, $object_id, $meta_type = 'post' ) {
450
+ foreach ( $fields as $field_args ) {
451
+ $key = $field_args['name'];
452
+
453
+ if ( 'checkbox' == $field_args['type'] ) {
454
+ $new_values = isset( $data[$key] ) ? $data[$key] : array();
455
+
456
+ $old_values = get_metadata( $meta_type, $object_id, $key );
457
+
458
+ foreach ( array_diff( $new_values, $old_values ) as $value )
459
+ add_metadata( $meta_type, $object_id, $key, $value );
460
+
461
+ foreach ( array_diff( $old_values, $new_values ) as $value )
462
+ delete_metadata( $meta_type, $object_id, $key, $value );
463
+ } else {
464
+ $value = $data[$key];
465
+
466
+ if ( '' === $value )
467
+ delete_metadata( $meta_type, $object_id, $key );
468
+ else
469
+ update_metadata( $meta_type, $object_id, $key, $value );
470
+ }
471
+ }
472
+ }
473
+
474
+ private static function set_value( &$arr, $name, $value ) {
475
+ $name = (array) $name;
476
+
477
+ $final_key = array_pop( $name );
478
+
479
+ while ( !empty( $name ) ) {
480
+ $key = array_shift( $name );
481
+
482
+ if ( !isset( $arr[ $key ] ) )
483
+ $arr[ $key ] = array();
484
+
485
+ $arr =& $arr[ $key ];
486
+ }
487
+
488
+ $arr[ $final_key ] = $value;
489
+ }
490
+
491
+ private static function is_associative( $array ) {
492
+ $keys = array_keys( $array );
493
+ return array_keys( $keys ) !== $keys;
494
+ }
495
+ }
496
+
497
+
498
+ /**
499
+ * A wrapper for scbForms, containing the formdata
500
+ */
501
+ class scbForm {
502
+ protected $data = array();
503
+ protected $prefix = array();
504
+
505
+ function __construct( $data, $prefix = false ) {
506
+ if ( is_array( $data ) )
507
+ $this->data = $data;
508
+
509
+ if ( $prefix )
510
+ $this->prefix = (array) $prefix;
511
+ }
512
+
513
+ function traverse_to( $path ) {
514
+ $data = scbForms::get_value( $path, $this->data );
515
+
516
+ $prefix = array_merge( $this->prefix, (array) $path );
517
+
518
+ return new scbForm( $data, $prefix );
519
+ }
520
+
521
+ function input( $args ) {
522
+ $value = scbForms::get_value( $args['name'], $this->data );
523
+
524
+ if ( !empty( $this->prefix ) ) {
525
+ $args['name'] = array_merge( $this->prefix, (array) $args['name'] );
526
+ }
527
+
528
+ return scbForms::input_with_value( $args, $value );
529
+ }
530
+ }
531
+
scb/Hooks.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class scbHooks {
4
+ private static $mangle_name;
5
+
6
+ public static function add( $class ) {
7
+ self::_do( 'add_filter', $class );
8
+ }
9
+
10
+ public static function remove( $class ) {
11
+ self::_do( 'remove_filter', $class );
12
+ }
13
+
14
+ public static function debug( $class, $mangle_name = false ) {
15
+ self::$mangle_name = $mangle_name;
16
+
17
+ echo "<pre>";
18
+ self::_do( array( __CLASS__, '_print' ), $class );
19
+ echo "</pre>";
20
+ }
21
+
22
+ private static function _print( $tag, $callback, $prio, $argc ) {
23
+ $static = !is_object( $callback[0] );
24
+
25
+ if ( self::$mangle_name )
26
+ $class = $static ? '__CLASS__' : '$this';
27
+ else if ( $static )
28
+ $class = "'" . $callback[0] . "'";
29
+ else
30
+ $class = '$' . get_class( $callback[0] );
31
+
32
+ $func = "array( $class, '$callback[1]' )";
33
+
34
+ echo "add_filter( '$tag', $func";
35
+
36
+ if ( $prio != 10 || $argc > 1 ) {
37
+ echo ", $prio";
38
+
39
+ if ( $argc > 1 )
40
+ echo ", $argc";
41
+ }
42
+
43
+ echo " );\n";
44
+ }
45
+
46
+ private static function _do( $action, $class ) {
47
+ $reflection = new ReflectionClass( $class );
48
+
49
+ foreach ( $reflection->getMethods() as $method ) {
50
+ if ( $method->isPublic() && !$method->isConstructor() ) {
51
+ $comment = $method->getDocComment();
52
+
53
+ if ( preg_match( '/@nohook[ \t\*\n]+/', $comment ) ) {
54
+ continue;
55
+ }
56
+
57
+ preg_match_all( '/@hook:?\s+([^\s]+)/', $comment, $matches ) ? $matches[1] : $method->name;
58
+ if ( empty( $matches[1] ) )
59
+ $hooks = array( $method->name );
60
+ else
61
+ $hooks = $matches[1];
62
+
63
+ $priority = preg_match( '/@priority:?\s+(\d+)/', $comment, $matches ) ? $matches[1] : 10;
64
+
65
+ foreach ( $hooks as $hook ) {
66
+ call_user_func( $action, $hook, array( $class, $method->name ), $priority, $method->getNumberOfParameters() );
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+
scb/Options.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Container for an array of options
4
+
5
+ class scbOptions {
6
+
7
+ protected $key; // the option name
8
+
9
+ protected $defaults; // the default values
10
+
11
+ public $wp_filter_id; // used by WP hooks
12
+
13
+ /**
14
+ * Create a new set of options
15
+ *
16
+ * @param string $key Option name
17
+ * @param string $file Reference to main plugin file
18
+ * @param array $defaults An associative array of default values (optional)
19
+ */
20
+ public function __construct( $key, $file, $defaults = array() ) {
21
+ $this->key = $key;
22
+ $this->defaults = $defaults;
23
+
24
+ if ( $file ) {
25
+ scbUtil::add_activation_hook( $file, array( $this, '_activation' ) );
26
+ scbUtil::add_uninstall_hook( $file, array( $this, 'delete' ) );
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Get option name
32
+ */
33
+ public function get_key() {
34
+ return $this->key;
35
+ }
36
+
37
+ /**
38
+ * Get option values for one, many or all fields
39
+ *
40
+ * @param string|array $field The field(s) to get
41
+ * @return mixed Whatever is in those fields
42
+ */
43
+ public function get( $field = '' ) {
44
+ $data = get_option( $this->key, array() );
45
+
46
+ $data = array_merge( $this->defaults, $data );
47
+
48
+ return $this->_get( $field, $data );
49
+ }
50
+
51
+ /**
52
+ * Get default values for one, many or all fields
53
+ *
54
+ * @param string|array $field The field( s ) to get
55
+ * @return mixed Whatever is in those fields
56
+ */
57
+ public function get_defaults( $field = '' ) {
58
+ return $this->_get( $field, $this->defaults );
59
+ }
60
+
61
+ /**
62
+ * Set all data fields, certain fields or a single field
63
+ *
64
+ * @param string|array $field The field to update or an associative array
65
+ * @param mixed $value The new value ( ignored if $field is array )
66
+ * @return null
67
+ */
68
+ public function set( $field, $value = '' ) {
69
+ if ( is_array( $field ) )
70
+ $newdata = $field;
71
+ else
72
+ $newdata = array( $field => $value );
73
+
74
+ $this->update( array_merge( $this->get(), $newdata ) );
75
+ }
76
+
77
+ /**
78
+ * Reset option to defaults
79
+ *
80
+ * @return null
81
+ */
82
+ public function reset() {
83
+ $this->update( $this->defaults, false );
84
+ }
85
+
86
+ /**
87
+ * Remove any keys that are not in the defaults array
88
+ *
89
+ * @return bool
90
+ */
91
+ public function cleanup() {
92
+ $this->update( $this->_clean( $this->get() ) );
93
+ }
94
+
95
+ /**
96
+ * Update raw data
97
+ *
98
+ * @param mixed $newdata
99
+ * @param bool $clean wether to remove unrecognized keys or not
100
+ * @return null
101
+ */
102
+ public function update( $newdata, $clean = true ) {
103
+ if ( $clean )
104
+ $newdata = $this->_clean( $newdata );
105
+
106
+ update_option( $this->key, array_merge( $this->get(), $newdata ) );
107
+ }
108
+
109
+ /**
110
+ * Delete the option
111
+ *
112
+ * @return null
113
+ */
114
+ public function delete() {
115
+ delete_option( $this->key );
116
+ }
117
+
118
+
119
+ //_____INTERNAL METHODS_____
120
+
121
+
122
+ // Saves an extra query
123
+ function _activation() {
124
+ add_option( $this->key, $this->defaults );
125
+ }
126
+
127
+ // Keep only the keys defined in $this->defaults
128
+ private function _clean( $data ) {
129
+ return wp_array_slice_assoc( $data, array_keys( $this->defaults ) );
130
+ }
131
+
132
+ // Get one, more or all fields from an array
133
+ private function &_get( $field, $data ) {
134
+ if ( empty( $field ) )
135
+ return $data;
136
+
137
+ if ( is_string( $field ) )
138
+ return $data[$field];
139
+
140
+ foreach ( $field as $key )
141
+ if ( isset( $data[$key] ) )
142
+ $result[] = $data[$key];
143
+
144
+ return $result;
145
+ }
146
+
147
+ // Magic method: $options->field
148
+ function __get( $field ) {
149
+ return $this->get( $field );
150
+ }
151
+
152
+ // Magic method: $options->field = $value
153
+ function __set( $field, $value ) {
154
+ $this->set( $field, $value );
155
+ }
156
+
157
+ // Magic method: isset( $options->field )
158
+ function __isset( $field ) {
159
+ $data = $this->get();
160
+ return isset( $data[$field] );
161
+ }
162
+ }
163
+
scb/Table.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Takes care of creating, updating and deleting database tables
4
+
5
+ class scbTable {
6
+ protected $name;
7
+ protected $columns;
8
+ protected $upgrade_method;
9
+
10
+ function __construct( $name, $file, $columns, $upgrade_method = 'dbDelta' ) {
11
+ $this->name = $name;
12
+ $this->columns = $columns;
13
+ $this->upgrade_method = $upgrade_method;
14
+
15
+ scb_register_table( $name );
16
+
17
+ if ( $file ) {
18
+ scbUtil::add_activation_hook( $file, array( $this, 'install' ) );
19
+ scbUtil::add_uninstall_hook( $file, array( $this, 'uninstall' ) );
20
+ }
21
+ }
22
+
23
+ function install() {
24
+ scb_install_table( $this->name, $this->columns, $this->upgrade_method );
25
+ }
26
+
27
+ function uninstall() {
28
+ scb_uninstall_table( $this->name );
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Register a table with $wpdb
34
+ *
35
+ * @param string $key The key to be used on the $wpdb object
36
+ * @param string $name The actual name of the table, without $wpdb->prefix
37
+ */
38
+ function scb_register_table( $key, $name = false ) {
39
+ global $wpdb;
40
+
41
+ if ( !$name )
42
+ $name = $key;
43
+
44
+ $wpdb->tables[] = $name;
45
+ $wpdb->$key = $wpdb->prefix . $name;
46
+ }
47
+
48
+ function scb_install_table( $key, $columns, $upgrade_method = 'dbDelta' ) {
49
+ global $wpdb;
50
+
51
+ $full_table_name = $wpdb->$key;
52
+
53
+ $charset_collate = '';
54
+ if ( $wpdb->has_cap( 'collation' ) ) {
55
+ if ( ! empty( $wpdb->charset ) )
56
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
57
+ if ( ! empty( $wpdb->collate ) )
58
+ $charset_collate .= " COLLATE $wpdb->collate";
59
+ }
60
+
61
+ if ( 'dbDelta' == $upgrade_method ) {
62
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
63
+ dbDelta( "CREATE TABLE $full_table_name ( $columns ) $charset_collate" );
64
+ return;
65
+ }
66
+
67
+ if ( 'delete_first' == $upgrade_method )
68
+ $wpdb->query( "DROP TABLE IF EXISTS $full_table_name;" );
69
+
70
+ $wpdb->query( "CREATE TABLE IF NOT EXISTS $full_table_name ( $columns ) $charset_collate;" );
71
+ }
72
+
73
+ function scb_uninstall_table( $key ) {
74
+ global $wpdb;
75
+
76
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->$key );
77
+ }
78
+
scb/Util.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Various utilities
4
+
5
+ class scbUtil {
6
+
7
+ // Force script enqueue
8
+ static function do_scripts( $handles ) {
9
+ global $wp_scripts;
10
+
11
+ if ( ! is_a( $wp_scripts, 'WP_Scripts' ) )
12
+ $wp_scripts = new WP_Scripts();
13
+
14
+ $wp_scripts->do_items( ( array ) $handles );
15
+ }
16
+
17
+ // Force style enqueue
18
+ static function do_styles( $handles ) {
19
+ self::do_scripts( 'jquery' );
20
+
21
+ global $wp_styles;
22
+
23
+ if ( ! is_a( $wp_styles, 'WP_Styles' ) )
24
+ $wp_styles = new WP_Styles();
25
+
26
+ ob_start();
27
+ $wp_styles->do_items( ( array ) $handles );
28
+ $content = str_replace( array( "'", "\n" ), array( '"', '' ), ob_get_clean() );
29
+
30
+ echo "<script type='text/javascript'>\n";
31
+ echo "jQuery(function ($) { $('head').prepend('$content'); });\n";
32
+ echo "</script>";
33
+ }
34
+
35
+ // Enable delayed activation; to be used with scb_init()
36
+ static function add_activation_hook( $plugin, $callback ) {
37
+ if ( defined( 'SCB_LOAD_MU' ) )
38
+ register_activation_hook( $plugin, $callback );
39
+ else
40
+ add_action( 'scb_activation_' . plugin_basename( $plugin ), $callback );
41
+ }
42
+
43
+ // For debugging
44
+ static function do_activation( $plugin ) {
45
+ do_action( 'scb_activation_' . plugin_basename( $plugin ) );
46
+ }
47
+
48
+ // Allows more than one uninstall hooks.
49
+ // Also prevents an UPDATE query on each page load.
50
+ static function add_uninstall_hook( $plugin, $callback ) {
51
+ if ( !is_admin() )
52
+ return;
53
+
54
+ register_uninstall_hook( $plugin, '__return_false' ); // dummy
55
+
56
+ add_action( 'uninstall_' . plugin_basename( $plugin ), $callback );
57
+ }
58
+
59
+ // For debugging
60
+ static function do_uninstall( $plugin ) {
61
+ do_action( 'uninstall_' . plugin_basename( $plugin ) );
62
+ }
63
+
64
+ // Get the current, full URL
65
+ static function get_current_url() {
66
+ return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
67
+ }
68
+
69
+ // Apply a function to each element of a ( nested ) array recursively
70
+ static function array_map_recursive( $callback, $array ) {
71
+ array_walk_recursive( $array, array( __CLASS__, 'array_map_recursive_helper' ), $callback );
72
+
73
+ return $array;
74
+ }
75
+
76
+ static function array_map_recursive_helper( &$val, $key, $callback ) {
77
+ $val = call_user_func( $callback, $val );
78
+ }
79
+
80
+ // Extract certain $keys from $array
81
+ static function array_extract( $array, $keys ) {
82
+ _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'WP 3.1', 'wp_array_slice_assoc()' );
83
+ return wp_array_slice_assoc( $array, $keys );
84
+ }
85
+
86
+ // Extract a certain value from a list of arrays
87
+ static function array_pluck( $array, $key ) {
88
+ _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'WP 3.1', 'wp_list_pluck()' );
89
+ return wp_list_pluck( $array, $key );
90
+ }
91
+
92
+ // Transform a list of objects into an associative array
93
+ static function objects_to_assoc( $objects, $key, $value ) {
94
+ _deprecated_function( __CLASS__ . '::' . __FUNCTION__, 'r41', 'scb_list_fold()' );
95
+ return scb_list_fold( $objects, $key, $value );
96
+ }
97
+
98
+ // Prepare an array for an IN statement
99
+ static function array_to_sql( $values ) {
100
+ foreach ( $values as &$val )
101
+ $val = "'" . esc_sql( trim( $val ) ) . "'";
102
+
103
+ return implode( ',', $values );
104
+ }
105
+
106
+ // Example: split_at( '</', '<a></a>' ) => array( '<a>', '</a>' )
107
+ static function split_at( $delim, $str ) {
108
+ $i = strpos( $str, $delim );
109
+
110
+ if ( false === $i )
111
+ return false;
112
+
113
+ $start = substr( $str, 0, $i );
114
+ $finish = substr( $str, $i );
115
+
116
+ return array( $start, $finish );
117
+ }
118
+ }
119
+
120
+ // Return a standard admin notice
121
+ function scb_admin_notice( $msg, $class = 'updated' ) {
122
+ return "<div class='$class fade'><p>$msg</p></div>\n";
123
+ }
124
+
125
+ // Transform a list of objects into an associative array
126
+ function scb_list_fold( $list, $key, $value ) {
127
+ $r = array();
128
+
129
+ if ( is_array( reset( $list ) ) ) {
130
+ foreach ( $list as $item )
131
+ $r[ $item[ $key ] ] = $item[ $value ];
132
+ } else {
133
+ foreach ( $list as $item )
134
+ $r[ $item->$key ] = $item->$value;
135
+ }
136
+
137
+ return $r;
138
+ }
139
+
140
+
141
+ //_____Minimalist HTML framework_____
142
+
143
+ /**
144
+ * Generate an HTML tag. Atributes are escaped. Content is NOT escaped.
145
+ */
146
+ if ( ! function_exists( 'html' ) ):
147
+ function html( $tag ) {
148
+ $args = func_get_args();
149
+
150
+ $tag = array_shift( $args );
151
+
152
+ if ( is_array( $args[0] ) ) {
153
+ $closing = $tag;
154
+ $attributes = array_shift( $args );
155
+ foreach ( $attributes as $key => $value ) {
156
+ if ( false === $value )
157
+ continue;
158
+
159
+ if ( true === $value )
160
+ $value = $key;
161
+
162
+ $tag .= ' ' . $key . '="' . esc_attr( $value ) . '"';
163
+ }
164
+ } else {
165
+ list( $closing ) = explode( ' ', $tag, 2 );
166
+ }
167
+
168
+ if ( in_array( $closing, array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta' ) ) ) {
169
+ return "<{$tag} />";
170
+ }
171
+
172
+ $content = implode( '', $args );
173
+
174
+ return "<{$tag}>{$content}</{$closing}>";
175
+ }
176
+ endif;
177
+
178
+ // Generate an <a> tag
179
+ if ( ! function_exists( 'html_link' ) ):
180
+ function html_link( $url, $title = '' ) {
181
+ if ( empty( $title ) )
182
+ $title = $url;
183
+
184
+ return html( 'a', array( 'href' => $url ), $title );
185
+ }
186
+ endif;
187
+
188
+
189
+ //_____Compatibility layer_____
190
+
191
+ // WP < ?
192
+ if ( ! function_exists( 'set_post_field' ) ) :
193
+ function set_post_field( $field, $value, $post_id ) {
194
+ global $wpdb;
195
+
196
+ $post_id = absint( $post_id );
197
+ $value = sanitize_post_field( $field, $value, $post_id, 'db' );
198
+
199
+ return $wpdb->update( $wpdb->posts, array( $field => $value ), array( 'ID' => $post_id ) );
200
+ }
201
+ endif;
202
+
scb/Widget.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Adds compatibility methods between WP_Widget and scbForms
4
+
5
+ abstract class scbWidget extends WP_Widget {
6
+ protected $defaults = array();
7
+
8
+ private static $scb_widgets = array();
9
+
10
+ static function init( $class, $file = '', $base = '' ) {
11
+ self::$scb_widgets[] = $class;
12
+
13
+ add_action( 'widgets_init', array( __CLASS__, '_scb_register' ) );
14
+
15
+ // for auto-uninstall
16
+ if ( $file && $base && class_exists( 'scbOptions' ) )
17
+ new scbOptions( "widget_$base", $file );
18
+ }
19
+
20
+ static function _scb_register() {
21
+ foreach ( self::$scb_widgets as $widget )
22
+ register_widget( $widget );
23
+ }
24
+
25
+ // A pre-filled method, for convenience
26
+ function widget( $args, $instance ) {
27
+ $instance = wp_parse_args( $instance, $this->defaults );
28
+
29
+ extract( $args );
30
+
31
+ echo $before_widget;
32
+
33
+ $title = apply_filters( 'widget_title', isset( $instance['title'] ) ? $instance['title'] : '', $instance, $this->id_base );
34
+
35
+ if ( ! empty( $title ) )
36
+ echo $before_title . $title . $after_title;
37
+
38
+ $this->content( $instance );
39
+
40
+ echo $after_widget;
41
+ }
42
+
43
+ // This is where the actual widget content goes
44
+ function content( $instance ) {}
45
+
46
+
47
+ //_____HELPER METHODS_____
48
+
49
+
50
+ // See scbForms::input()
51
+ // Allows extra parameter $args['title']
52
+ protected function input( $args, $formdata = array() ) {
53
+ $prefix = array( 'widget-' . $this->id_base, $this->number );
54
+
55
+ $form = new scbForm( $formdata, $prefix );
56
+
57
+ // Add default class
58
+ if ( !isset( $args['extra'] ) && 'text' == $args['type'] )
59
+ $args['extra'] = array( 'class' => 'widefat' );
60
+
61
+ // Add default label position
62
+ if ( !in_array( $args['type'], array( 'checkbox', 'radio' ) ) && empty( $args['desc_pos'] ) )
63
+ $args['desc_pos'] = 'before';
64
+
65
+ $name = $args['name'];
66
+
67
+ if ( !is_array( $name ) && '[]' == substr( $name, -2 ) )
68
+ $name = array( substr( $name, 0, -2 ), '' );
69
+
70
+ $args['name'] = $name;
71
+
72
+ return $form->input( $args );
73
+ }
74
+ }
75
+
scb/load.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $GLOBALS['_scb_data'] = array( 53, __FILE__, array(
4
+ 'scbUtil', 'scbOptions', 'scbForms', 'scbTable',
5
+ 'scbWidget', 'scbAdminPage', 'scbBoxesPage',
6
+ 'scbCron', 'scbHooks',
7
+ ) );
8
+
9
+ if ( !class_exists( 'scbLoad4' ) ) :
10
+ /**
11
+ * The main idea behind this class is to load the most recent version of the scb classes available.
12
+ *
13
+ * It waits until all plugins are loaded and then does some crazy hacks to make activation hooks work.
14
+ */
15
+ class scbLoad4 {
16
+
17
+ private static $candidates = array();
18
+ private static $classes;
19
+ private static $callbacks = array();
20
+
21
+ private static $loaded;
22
+
23
+ static function init( $callback = '' ) {
24
+ list( $rev, $file, $classes ) = $GLOBALS['_scb_data'];
25
+
26
+ self::$candidates[$file] = $rev;
27
+ self::$classes[$file] = $classes;
28
+
29
+ if ( !empty( $callback ) ) {
30
+ self::$callbacks[$file] = $callback;
31
+
32
+ add_action( 'activate_plugin', array( __CLASS__, 'delayed_activation' ) );
33
+ }
34
+
35
+ // TODO: don't load when activating a plugin ?
36
+ add_action( 'plugins_loaded', array( __CLASS__, 'load' ), 9, 0 );
37
+ }
38
+
39
+ static function delayed_activation( $plugin ) {
40
+ $plugin_dir = dirname( $plugin );
41
+
42
+ if ( '.' == $plugin_dir )
43
+ return;
44
+
45
+ foreach ( self::$callbacks as $file => $callback ) {
46
+ if ( dirname( dirname( plugin_basename( $file ) ) ) == $plugin_dir ) {
47
+ self::load( false );
48
+ call_user_func( $callback );
49
+ do_action( 'scb_activation_' . $plugin );
50
+ break;
51
+ }
52
+ }
53
+ }
54
+
55
+ static function load( $do_callbacks = true ) {
56
+ arsort( self::$candidates );
57
+
58
+ $file = key( self::$candidates );
59
+
60
+ $path = dirname( $file ) . '/';
61
+
62
+ foreach ( self::$classes[$file] as $class_name ) {
63
+ if ( class_exists( $class_name ) )
64
+ continue;
65
+
66
+ $fpath = $path . substr( $class_name, 3 ) . '.php';
67
+ if ( file_exists( $fpath ) ) {
68
+ include $fpath;
69
+ self::$loaded[] = $fpath;
70
+ }
71
+ }
72
+
73
+ if ( $do_callbacks )
74
+ foreach ( self::$callbacks as $callback )
75
+ call_user_func( $callback );
76
+ }
77
+
78
+ static function get_info() {
79
+ arsort( self::$candidates );
80
+
81
+ return array( self::$loaded, self::$candidates );
82
+ }
83
+ }
84
+ endif;
85
+
86
+ if ( !function_exists( 'scb_init' ) ) :
87
+ function scb_init( $callback = '' ) {
88
+ scbLoad4::init( $callback );
89
+ }
90
+ endif;
91
+