Lazy Load - Version 0.1

Version Description

  • Initial working version
Download this release

Release Info

Developer batmoo
Plugin Icon wp plugin Lazy Load
Version 0.1
Comparing to
See all releases

Version 0.1

images/1x1.trans.gif ADDED
Binary file
js/jquery.sonar.js ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ An elem for determining if an elem is within a certain
3
+ distance from the edge above or below the screen, and attaching
4
+ a function to execute once the elem is in view.
5
+
6
+ General Usage:
7
+
8
+ * Place the library anywhere in your JavaScript code before you
9
+ intend to call the function.
10
+
11
+ * To initialize Sonar with a different default distance, modify
12
+ the sonar = Sonar() line immediately following the Sonar
13
+ library definition. Example:
14
+
15
+ sonar=Sonar(100); // Initializes Sonar with a 100px default distance.
16
+
17
+ Note:
18
+
19
+ * The default distance is 0 pixels.
20
+
21
+
22
+ sonar.detect() Usage
23
+
24
+ * Use sonar.detect(elem, distance) to check if the
25
+ elem is within screen boundaries.
26
+
27
+ @elem - The elem you want to detect visibility.
28
+ @distance - The distance from the screen edge that should
29
+ count in the check. Uses default distance if not specified.
30
+
31
+ * Note: sonar.detect() adds a property to
32
+ ojbects called sonarElemTop. Test to ensure there
33
+ aren't any conflicts with your code. If there
34
+ are, rename sonarElemTop to something else in the code.
35
+
36
+ * sonar.detect() returns:
37
+ true if the elem is within the screen boundaries
38
+ false if th elem is out of the screen boundaries
39
+
40
+ Example:
41
+
42
+ Here's how to check if an advertisment is visible on a
43
+ page that has the id, "ad".
44
+
45
+ if (sonar.detect(document.getElementById("ad")))
46
+ {
47
+ alert('The ad is visible on screen!');
48
+ }
49
+ else
50
+ {
51
+ alert ('The ad is not on screen!);
52
+ }
53
+
54
+ sonar.add() Usage
55
+
56
+ * This method stores elems that are then polled
57
+ on user scroll by the Sonar.detect() method.
58
+
59
+ * Polling initializes once the sonar.add() method is passed
60
+ an elem with the following properties:
61
+
62
+ obj : A reference to the elem to observe until it is within
63
+ the specified distance (px).
64
+
65
+ id : An alternative to the obj parameter, an "id" can be used
66
+ to grab the elem to observe.
67
+
68
+ call: The function to call when the elem is within the
69
+ specified distance (px). The @elem argument will
70
+ include the elem that triggered the callback.
71
+
72
+ px : The specified distance to include as being visible on
73
+ screen. This property is optional (default is 0).
74
+
75
+ Example:
76
+
77
+ sonar.add(
78
+ {
79
+ obj: document.getElementById("0026-get-out-the-way"),
80
+ call: function(elem) // elem will include the elem that triggered the function.
81
+ {
82
+ swfelem.embedSWF("../player.swf", "0026-get-out-the-way", "640", "500", "9.0.0",
83
+ {}, {file: "0026-get-out-the-way.flv", fullscreen: true},
84
+ {allowfullscreen: true, allowscriptaccess: "always"});
85
+ },
86
+ px: 400
87
+ });
88
+
89
+ You can also specify an id tag to be grabbed instead of the elem:
90
+
91
+ sonar.add(
92
+ {
93
+ id: "0026-get-out-the-way",
94
+ call: function(elem) // elem will include the elem that triggered the function.
95
+ {
96
+ swfelem.embedSWF("../player.swf", "0026-get-out-the-way", "640", "500", "9.0.0",
97
+ {}, {file: "0026-get-out-the-way.flv", fullscreen: true},
98
+ {allowfullscreen: true, allowscriptaccess: "always"});
99
+ },
100
+ px: 400
101
+ });
102
+
103
+ Notes:
104
+
105
+ * Setting the body or html of your page to 100% will cause sonar to have
106
+ an invalid height calculation in Firefox. It is recommended that you
107
+ do not set this CSS property.
108
+
109
+ Example:
110
+
111
+ html, body {
112
+ height:100%; // Do not do this.
113
+ }
114
+
115
+ * If you want to set the default distance to something other
116
+ than 0, either update the property directly in the code or
117
+ you can do this:
118
+
119
+ sonar.blip.d = 100; // Where 100 = 100 pixels above and below the screen edge.
120
+
121
+ * Sleep well at night knowing Sonar automatically cleans up the
122
+ event listeners on the scroll event once all calls have executed.
123
+
124
+ Code History:
125
+
126
+ v3 :: 8/14/2009 - David Artz (david.artz@corp.aol.com)
127
+ * Fixed a bug in the polling code where splicing caused our
128
+ for loop to skip over the next iteration in the loop. This
129
+ caused some images in the poll to be detected when they
130
+ should have been.
131
+ * Re-factored Sonar to use the "Module" JavaScript library
132
+ pattern, making our private variables and functions more
133
+ private and inaccessible from the public interface.
134
+ * Updated the sonar.add() function to return true or false,
135
+ useful for determining if Sonar added the elem to the
136
+ poll or executed its callback immediately.
137
+
138
+ v2 :: 3/24/2009 - David Artz (david.artz@corp.aol.com)
139
+ * Added support for IE 8.
140
+ * Updated the way scroll top and screen height are detected, now
141
+ works in IE/FF/Safari quirks mode.
142
+ * Added null check for IE, it was polling for an elem that had recently
143
+ been spliced out of the array. Nasty.
144
+ * Modified for loop to use standard syntax. for (i in x) is known to be
145
+ buggy with JS frameworks that override arrays.
146
+ * Added sonar.b property to cache the body element (improving lookup time).
147
+
148
+ v1 :: 11/18/2008 - David Artz (david.artz@corp.aol.com)
149
+ * Officially released code for general use.
150
+
151
+ */
152
+
153
+ (function( $, win, doc, undefined ){
154
+
155
+ $.fn.sonar = function( distance, full ){
156
+ // No callbacks, return the results from Sonar for
157
+ // the first element in the stack.
158
+ if ( typeof distance === "boolean" ) {
159
+ full = distance;
160
+ distance = undefined;
161
+ }
162
+
163
+ return $.sonar( this[0], distance, full );
164
+ };
165
+
166
+ var body = doc.body,
167
+ $win = $(win),
168
+
169
+ onScreenEvent = "scrollin",
170
+ offScreenEvent = "scrollout",
171
+
172
+ detect = function( elem, distance, full ){
173
+
174
+ if ( elem ) {
175
+
176
+ // Cache the body elem in our private global.
177
+ body || ( body = doc.body );
178
+
179
+ var parentElem = elem, // Clone the elem for use in our loop.
180
+
181
+ elemTop = 0, // The resets the calculated elem top to 0.
182
+
183
+ // Used to recalculate elem.sonarElemTop if body height changes.
184
+ bodyHeight = body.offsetHeight,
185
+
186
+ // NCZ: I don't think you need innerHeight, I believe all major browsers support clientHeight.
187
+ screenHeight = win.innerHeight || doc.documentElement.clientHeight || body.clientHeight || 0, // Height of the screen.
188
+
189
+ // NCZ: I don't think you need pageYOffset, I believe all major browsers support scrollTop.
190
+ scrollTop = doc.documentElement.scrollTop || win.pageYOffset || body.scrollTop || 0, // How far the user scrolled down.
191
+ elemHeight = elem.offsetHeight || 0; // Height of the element.
192
+
193
+ // If our custom "sonarTop" variable is undefined, or the document body
194
+ // height has changed since the last time we ran sonar.detect()...
195
+ if ( !elem.sonarElemTop || elem.sonarBodyHeight !== bodyHeight ) {
196
+
197
+ // Loop through the offsetParents to calculate it.
198
+ if ( parentElem.offsetParent ) {
199
+ do {
200
+ elemTop += parentElem.offsetTop;
201
+ }
202
+ while ( parentElem = parentElem.offsetParent );
203
+ }
204
+
205
+ // Set the custom property (sonarTop) to avoid future attempts to calculate
206
+ // the distance on this elem from the top of the page.
207
+ elem.sonarElemTop = elemTop;
208
+
209
+ // Along the same lines, store the body height when we calculated
210
+ // the elem's top.
211
+ elem.sonarBodyHeight = bodyHeight;
212
+ }
213
+
214
+ // If no distance was given, assume 0.
215
+ distance = distance === undefined ? 0 : distance;
216
+
217
+ // Dump all calculated variables.
218
+ /*
219
+ console.dir({
220
+ elem: elem,
221
+ sonarElemTop: elem.sonarElemTop,
222
+ elemHeight: elemHeight,
223
+ scrollTop: scrollTop,
224
+ screenHeight: screenHeight,
225
+ distance: distance,
226
+ full: full
227
+ });
228
+ */
229
+
230
+ // If elem bottom is above the screen top and
231
+ // the elem top is below the screen bottom, it's false.
232
+ // If full is specified, it si subtracted or added
233
+ // as needed from the element's height.
234
+ return (!(elem.sonarElemTop + (full ? 0 : elemHeight) < scrollTop - distance) &&
235
+ !(elem.sonarElemTop + (full ? elemHeight : 0) > scrollTop + screenHeight + distance));
236
+ }
237
+ },
238
+
239
+ // Container for elems needing to be polled.
240
+ pollQueue = {},
241
+
242
+ // Indicates if scroll events are bound to the poll.
243
+ pollActive = 0,
244
+
245
+ // Used for debouncing.
246
+ pollId,
247
+
248
+ // Function that handles polling when the user scrolls.
249
+ poll = function(){
250
+
251
+ // Debouncing speed optimization. Essentially prevents
252
+ // poll requests from queue'ing up and overloading
253
+ // the scroll event listener.
254
+ pollId && clearTimeout( pollId );
255
+ pollId = setTimeout(function(){
256
+
257
+ var elem,
258
+ elems,
259
+ screenEvent,
260
+ options,
261
+ detected,
262
+ i, l;
263
+
264
+ for ( screenEvent in pollQueue ) {
265
+
266
+ elems = pollQueue[ screenEvent ];
267
+
268
+ for (i = 0, l = elems.length; i < l; i++) {
269
+
270
+ options = elems[i];
271
+ elem = options.elem;
272
+
273
+ // console.log("Polling " + elem.id);
274
+
275
+ detected = detect( elem, options.px, options.full );
276
+
277
+ // If the elem is not detected (offscreen) or detected (onscreen)
278
+ // remove the elem from the queue and fire the callback.
279
+ if ( screenEvent === offScreenEvent ? !detected : detected ) {
280
+ // // console.log(screenEvent);
281
+ if (!options.tr) {
282
+
283
+ if ( elem[ screenEvent ] ) {
284
+ // console.log("triggered:" + elem.id);
285
+ // Trigger the onscreen or offscreen event depending
286
+ // on the desired event.
287
+ $(elem).trigger( screenEvent );
288
+
289
+ options.tr = 1;
290
+
291
+ // removeSonar was called on this element, clean it up
292
+ // instead of triggering the event.
293
+ } else {
294
+ // console.log("Deleting " + elem.id);
295
+
296
+ // Remove this object from the elem poll container.
297
+ elems.splice(i, 1);
298
+
299
+ // Decrement the counter and length because we just removed
300
+ // one from it.
301
+ i--;
302
+ l--;
303
+ }
304
+ }
305
+ } else {
306
+ options.tr = 0;
307
+ }
308
+ }
309
+ }
310
+
311
+ }, 0 ); // End setTimeout performance tweak.
312
+ },
313
+
314
+ removeSonar = function( elem, screenEvent ){
315
+ // console.log("Removing " + elem.id);
316
+ elem[ screenEvent ] = 0;
317
+ },
318
+
319
+ addSonar = function( elem, options ) {
320
+ // console.log("Really adding " + elem.id);
321
+ // Prepare arguments.
322
+ var distance = options.px,
323
+ full = options.full,
324
+ screenEvent = options.evt,
325
+ parent = win, // Getting ready to accept parents: options.parent || win,
326
+ detected = detect( elem, distance, full /*, parent */ ),
327
+ triggered = 0;
328
+
329
+ elem[ screenEvent ] = 1;
330
+
331
+ // If the elem is not detected (offscreen) or detected (onscreen)
332
+ // trigger the event and fire the callback immediately.
333
+ if ( screenEvent === offScreenEvent ? !detected : detected ) {
334
+ // console.log("Triggering " + elem.id + " " + screenEvent );
335
+ // Trigger the onscreen event at the next possible cycle.
336
+ // Artz: Ask the jQuery team why I needed to do this.
337
+ setTimeout(function(){
338
+ $(elem).trigger( screenEvent === offScreenEvent ? offScreenEvent : onScreenEvent );
339
+ }, 0);
340
+ triggered = 1;
341
+ // Otherwise, add it to the polling queue.
342
+ }
343
+
344
+ // console.log("Adding " + elem.id + " to queue.");
345
+ // Push the element and its callback into the poll queue.
346
+ pollQueue[ screenEvent ].push({
347
+ elem: elem,
348
+ px: distance,
349
+ full: full,
350
+ tr: triggered/* ,
351
+ parent: parent */
352
+ });
353
+
354
+ // Activate the poll if not currently activated.
355
+ if ( !pollActive ) {
356
+ $win.bind( "scroll", poll );
357
+ pollActive = 1;
358
+ }
359
+
360
+
361
+ // Call the prepare function if there, used to
362
+ // prepare the element if we detected it.
363
+ // Artz: Not implemented yet...used to preprocess elements in same loop.
364
+ /*
365
+ if ( prepCallback ) {
366
+ prepCallback.call( elem, elem, detected );
367
+ }
368
+ */
369
+ };
370
+
371
+ // Open sonar function up to the public.
372
+ $.sonar = detect;
373
+
374
+ pollQueue[ onScreenEvent ] = [];
375
+ $.event.special[ onScreenEvent ] = {
376
+
377
+ add: function( handleObj ) {
378
+ var data = handleObj.data || {},
379
+ elem = this;
380
+
381
+ if (!elem[onScreenEvent]){
382
+ addSonar(this, {
383
+ px: data.distance,
384
+ full: data.full,
385
+ evt: onScreenEvent /*,
386
+ parent: data.parent */
387
+ });
388
+ }
389
+ },
390
+
391
+ remove: function( handleObj ) {
392
+ removeSonar( this, onScreenEvent );
393
+ }
394
+
395
+ };
396
+
397
+ pollQueue[ offScreenEvent ] = [];
398
+ $.event.special[ offScreenEvent ] = {
399
+
400
+ add: function( handleObj ) {
401
+
402
+ var data = handleObj.data || {},
403
+ elem = this;
404
+
405
+ if (!elem[offScreenEvent]){
406
+ addSonar(elem, {
407
+ px: data.distance,
408
+ full: data.full,
409
+ evt: offScreenEvent /*,
410
+ parent: data.parent */
411
+ });
412
+ }
413
+ },
414
+
415
+ remove: function( handleObj ) {
416
+ removeSonar( this, offScreenEvent );
417
+ }
418
+ };
419
+
420
+ // console.log(pollQueue);
421
+ })( jQuery, window, document );
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);
js/lazy-load.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($) {
2
+ $( 'img[data-lazy-src]' ).bind( 'scrollin', { distance: 200 }, function() {
3
+ var img = this,
4
+ $img = jQuery(img),
5
+ src = $img.attr( 'data-lazy-src' );
6
+ $img.unbind( 'scrollin' ) // remove event binding
7
+ .hide()
8
+ .removeAttr( 'data-lazy-src' )
9
+ .attr( 'data-lazy-loaded', 'true' );;
10
+ img.src = src;
11
+ $img.fadeIn();
12
+ });
13
+ })(jQuery);
lazy-load.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: Lazy Load
4
+ * Description: Lazy load images to improve page load times. Uses jQuery.sonar to only load an image when it's visible in the viewport.
5
+ * Version: 0.1
6
+ *
7
+ * Code by the WordPress.com VIP team, TechCrunch 2011 Redesign team, and Jake Goldman (10up LLC).
8
+ * Uses jQuery.sonar by Dave Artz (AOL): http://www.artzstudio.com/files/jquery-boston-2010/jquery.sonar/
9
+ *
10
+ * License: GPL2
11
+ */
12
+
13
+ class LazyLoad_Images {
14
+
15
+ const version = '0.1';
16
+
17
+ var $base_url = '';
18
+
19
+ function __construct() {
20
+ add_action( 'wp_enqueue_scripts', array( $this, 'add_scripts' ) );
21
+ add_filter( 'the_content', array( $this, 'add_image_placeholders' ) );
22
+ }
23
+
24
+ function add_scripts() {
25
+ wp_enqueue_script( 'wpcom-lazy-load-images', $this->get_url( 'js/lazy-load.js' ), array( 'jquery', 'jquery-sonar' ), self::version, true );
26
+ wp_enqueue_script( 'jquery-sonar', $this->get_url( 'js/jquery.sonar.min.js' ), array( 'jquery' ), self::version, true );
27
+ }
28
+
29
+ function add_image_placeholders( $content ) {
30
+ // Don't lazyload for feeds, previews, mobile
31
+ if( is_feed() || ( function_exists( 'is_mobile' ) && is_mobile() ) || isset( $_GET['preview'] ) )
32
+ return $content;
33
+
34
+ // In case you want to change the placeholder image
35
+ $image = apply_filters( 'lazyload_images_placeholder_image', $this->get_url( 'images/1x1.trans.gif' ) );
36
+
37
+ // This is a pretty simple regex, but it works
38
+ $content = preg_replace( '#<img([^>]+?)src=#', sprintf( '<img${1}src="%s" data-lazy-src=', $image ), $content );
39
+
40
+ return $content;
41
+ }
42
+
43
+ function get_url( $path = '' ) {
44
+ return plugins_url( ltrim( $path, '/' ), __FILE__ );
45
+ }
46
+ }
47
+
48
+ new LazyLoad_Images;
readme.txt ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Lazy Load ===
2
+ Contributors: batmoo, automattic, jakemgold, get10up
3
+ Tags: lazy load, images, front-end optimization
4
+ Requires at least: 3.2
5
+ Tested up to: 3.3
6
+ Stable tag: 0.1
7
+
8
+ Lazy load images to improve page load times and server bandwidth. Images are loaded only when visible to the user.
9
+
10
+ == Description ==
11
+
12
+ Lazy load images to improve page load times. Uses jQuery.sonar to only load an image when it's visible in the viewport.
13
+
14
+ This plugin is an amalgamation of code written by the WordPress.com VIP team at Automattic, the TechCrunch 2011 Redesign team, and Jake Goldman (10up LLC).
15
+
16
+ Uses <a href="http://www.artzstudio.com/files/jquery-boston-2010/jquery.sonar/ ">jQuery.sonar</a> by Dave Artz (AOL).
17
+
18
+ == Installation ==
19
+
20
+ 1. Upload the plugin to your plugins directory
21
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
22
+ 1. Enjoy!
23
+
24
+ == Screenshots ==
25
+
26
+ No applicable screenshots
27
+
28
+ == Changelog ==
29
+
30
+ = 0.1 =
31
+
32
+ * Initial working version