]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/imageloader/imageloader.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / imageloader / imageloader.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('imageloader', function(Y) {
9
10 /**
11  * The ImageLoader Utility is a framework to dynamically load images according to certain triggers,
12  * enabling faster load times and a more responsive UI.
13  *
14  * @module imageloader
15  * @requires base-base, node-style, node-screen
16  */
17
18
19         /**
20          * A group for images. A group can have one time limit and a series of triggers. Thus the images belonging to this group must share these constraints.
21          * @class ImgLoadGroup
22          * @extends Base
23          * @constructor
24          */
25         Y.ImgLoadGroup = function() {
26                 // call init first, because it sets up local vars for storing attribute-related info
27                 this._init();
28                 Y.ImgLoadGroup.superclass.constructor.apply(this, arguments);
29         };
30
31         Y.ImgLoadGroup.NAME = 'imgLoadGroup';
32
33         Y.ImgLoadGroup.ATTRS = {
34                 
35                 /**
36                  * Name for the group. Only used to identify the group in logging statements.
37                  * @attribute name
38                  * @type String
39                  */
40                 name: {
41                         value: ''
42                 },
43
44                 /**
45                  * Time limit, in seconds, after which images are fetched regardless of trigger events.
46                  * @attribute timeLimit
47                  * @type Number
48                  */
49                 timeLimit: {
50                         value: null
51                 },
52
53                 /**
54                  * Distance below the fold for which images are loaded. Images are not loaded until they are at most this distance away from (or above) the fold.
55                  * This check is performed at page load (domready) and after any window scroll or window resize event (until all images are loaded).
56                  * @attribute foldDistance
57                  * @type Number
58                  */
59                 foldDistance: {
60                         validator: Y.Lang.isNumber,
61                         setter: function(val) { this._setFoldTriggers(); return val; },
62                         lazyAdd: false
63                 },
64
65                 /**
66                  * Class name that will identify images belonging to the group. This class name will be removed from each element in order to fetch images.
67                  * This class should have, in its CSS style definition, "<code>background:none !important;</code>".
68                  * @attribute className
69                  * @type String
70                  */
71                 className: {
72                         value: null,
73                         setter: function(name) { this._className = name; return name; },
74                         lazyAdd: false
75                 }
76
77         };
78
79         var groupProto = {
80
81                 /**
82                  * Initialize all private members needed for the group.
83                  * @method _init
84                  * @private
85                  */
86                 _init: function() {
87
88                         /**
89                          * Collection of triggers for this group.
90                          * Keeps track of each trigger's event handle, as returned from <code>Y.on</code>.
91                          * @property _triggers
92                          * @private
93                          * @type Array
94                          */
95                         this._triggers = [];
96
97                         /**
98                          * Collection of images (<code>Y.ImgLoadImgObj</code> objects) registered with this group, keyed by DOM id.
99                          * @property _imgObjs
100                          * @private
101                          * @type Object
102                          */
103                         this._imgObjs = {};
104
105                         /**
106                          * Timeout object to keep a handle on the time limit.
107                          * @property _timeout
108                          * @private
109                          * @type Object
110                          */
111                         this._timeout = null;
112
113                         /**
114                          * DOM elements having the class name that is associated with this group.
115                          * Elements are stored during the <code>_foldCheck</code> function and reused later during any subsequent <code>_foldCheck</code> calls - gives a slight performance improvement when the page fold is repeatedly checked.
116                          * @property _classImageEls
117                          * @private
118                          * @type Array
119                          */
120                         this._classImageEls = null;
121
122                         /**
123                          * Keep the CSS class name in a member variable for ease and speed.
124                          * @property _className
125                          * @private
126                          * @type String
127                          */
128                         this._className = null;
129
130                         /**
131                          * Boolean tracking whether the window scroll and window resize triggers have been set if this is a fold group.
132                          * @property _areFoldTriggersSet
133                          * @private
134                          * @type Boolean
135                          */
136                         this._areFoldTriggersSet = false;
137
138                         /**
139                          * The maximum pixel height of the document that has been made visible.
140                          * During fold checks, if the user scrolls up then there's no need to check for newly exposed images.
141                          * @property _maxKnownHLimit
142                          * @private
143                          * @type Int
144                          */
145                         this._maxKnownHLimit = 0;
146
147                         // add a listener to domready that will start the time limit
148                         Y.on('domready', this._onloadTasks, this);
149                 },
150
151                 /**
152                  * Adds a trigger to the group. Arguments are passed to <code>Y.on</code>.
153                  * @method addTrigger
154                  * @chainable
155                  * @param {Object} obj  The DOM object to attach the trigger event to
156                  * @param {String} type  The event type
157                  */
158                 addTrigger: function(obj, type) {
159                         if (! obj || ! type) {
160                                 return this;
161                         }
162
163
164                         /* Need to wrap the fetch function. Event Util can't distinguish prototyped functions of different instantiations.
165                          *   Leads to this scenario: groupA and groupZ both have window-scroll triggers. groupZ also has a 2-sec timeout (groupA has no timeout).
166                          *   groupZ's timeout fires; we remove the triggers. The detach call finds the first window-scroll event with Y.ILG.p.fetch, which is groupA's. 
167                          *   groupA's trigger is removed and never fires, leaving images unfetched.
168                          */
169                         var wrappedFetch = function() {
170                                 this.fetch();
171                         };
172                         this._triggers.push( Y.on(type, wrappedFetch, obj, this) );
173
174                         return this;
175                 },
176
177                 /**
178                  * Adds a custom event trigger to the group.
179                  * @method addCustomTrigger
180                  * @chainable
181                  * @param {String} name  The name of the event
182                  * @param {Object} obj  The object on which to attach the event. <code>obj</code> is optional - by default the event is attached to the <code>Y</code> instance
183                  */
184                 addCustomTrigger: function(name, obj) {
185                         if (! name) {
186                                 return this;
187                         }
188
189
190                         // see comment in addTrigger()
191                         var wrappedFetch = function() {
192                                 this.fetch();
193                         };
194                         if (Y.Lang.isUndefined(obj)) {
195                                 this._triggers.push( Y.on(name, wrappedFetch, this) );
196                         }
197                         else {
198                                 this._triggers.push( obj.on(name, wrappedFetch, this) );
199                         }
200
201                         return this;
202                 },
203
204                 /**
205                  * Sets the window scroll and window resize triggers for any group that is fold-conditional (i.e., has a fold distance set).
206                  * @method _setFoldTriggers
207                  * @private
208                  */
209                 _setFoldTriggers: function() {
210                         if (this._areFoldTriggersSet) {
211                                 return;
212                         }
213
214
215                         var wrappedFoldCheck = function() {
216                                 this._foldCheck();
217                         };
218                         this._triggers.push( Y.on('scroll', wrappedFoldCheck, window, this) );
219                         this._triggers.push( Y.on('resize', wrappedFoldCheck, window, this) );
220                         this._areFoldTriggersSet = true;
221                 },
222
223                 /**
224                  * Performs necessary setup at domready time.
225                  * Initiates time limit for group; executes the fold check for the images.
226                  * @method _onloadTasks
227                  * @private
228                  */
229                 _onloadTasks: function() {
230                         var timeLim = this.get('timeLimit');
231                         if (timeLim && timeLim > 0) {
232                                 this._timeout = setTimeout(this._getFetchTimeout(), timeLim * 1000);
233                         }
234
235                         if (! Y.Lang.isUndefined(this.get('foldDistance'))) {
236                                 this._foldCheck();
237                         }
238                 },
239
240                 /**
241                  * Returns the group's <code>fetch</code> method, with the proper closure, for use with <code>setTimeout</code>.
242                  * @method _getFetchTimeout
243                  * @return {Function}  group's <code>fetch</code> method
244                  * @private
245                  */
246                 _getFetchTimeout: function() {
247                         var self = this;
248                         return function() { self.fetch(); };
249                 },
250
251                 /**
252                  * Registers an image with the group.
253                  * Arguments are passed through to a <code>Y.ImgLoadImgObj</code> constructor; see that class' attribute documentation for detailed information. "<code>domId</code>" is a required attribute.
254                  * @method registerImage
255                  * @param {Object} *  A configuration object literal with attribute name/value pairs  (passed through to a <code>Y.ImgLoadImgObj</code> constructor)
256                  * @return {Object}  <code>Y.ImgLoadImgObj</code> that was registered
257                  */
258                 registerImage: function() {
259                         var domId = arguments[0].domId;
260                         if (! domId) {
261                                 return null;
262                         }
263
264
265                         this._imgObjs[domId] = new Y.ImgLoadImgObj(arguments[0]);
266                         return this._imgObjs[domId];
267                 },
268
269                 /**
270                  * Displays the images in the group.
271                  * This method is called when a trigger fires or the time limit expires; it shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
272                  * @method fetch
273                  */
274                 fetch: function() {
275
276                         // done with the triggers
277                         this._clearTriggers();
278
279                         // fetch whatever we need to by className
280                         this._fetchByClass();
281
282                         // fetch registered images
283                         for (var id in this._imgObjs) {
284                                 if (this._imgObjs.hasOwnProperty(id)) {
285                                         this._imgObjs[id].fetch();
286                                 }
287                         }
288                 },
289
290                 /**
291                  * Clears the timeout and all triggers associated with the group.
292                  * @method _clearTriggers
293                  * @private
294                  */
295                 _clearTriggers: function() {
296                         clearTimeout(this._timeout);
297                         // detach all listeners
298                         for (var i=0, len = this._triggers.length; i < len; i++) {
299                                 this._triggers[i].detach();
300                         }
301                 },
302
303                 /**
304                  * Checks the position of each image in the group. If any part of the image is within the specified distance (<code>foldDistance</code>) of the client viewport, the image is fetched immediately.
305                  * @method _foldCheck
306                  * @private
307                  */
308                 _foldCheck: function() {
309
310                         var allFetched = true,
311                             viewReg = Y.DOM.viewportRegion(),
312                             hLimit = viewReg.bottom + this.get('foldDistance'),
313                                         id, imgFetched, els, i, len;
314
315                         // unless we've uncovered new frontiers, there's no need to continue
316                         if (hLimit <= this._maxKnownHLimit) {
317                                 return;
318                         }
319                         this._maxKnownHLimit = hLimit;
320
321                         for (id in this._imgObjs) {
322                                 if (this._imgObjs.hasOwnProperty(id)) {
323                                         imgFetched = this._imgObjs[id].fetch(hLimit);
324                                         allFetched = allFetched && imgFetched;
325                                 }
326                         }
327
328                         // and by class
329                         if (this._className) {
330                                 if (this._classImageEls === null) {
331                                         // get all the relevant elements and store them
332                                         this._classImageEls = [];
333                                         els = Y.all('.' + this._className);
334                                         els.each( function(node) { this._classImageEls.push( { el: node, y: node.getY(), fetched: false } ); }, this);
335                                 }
336                                 els = this._classImageEls;
337                                 for (i=0, len = els.length; i < len; i++) {
338                                         if (els[i].fetched) {
339                                                 continue;
340                                         }
341                                         if (els[i].y && els[i].y <= hLimit) {
342                                                 els[i].el.removeClass(this._className);
343                                                 els[i].fetched = true;
344                                         }
345                                         else {
346                                                 allFetched = false;
347                                         }
348                                 }
349                         }
350                         
351                         // if allFetched, remove listeners
352                         if (allFetched) {
353                                 this._clearTriggers();
354                         }
355                 },
356
357                 /**
358                  * Finds all elements in the DOM with the class name specified in the group. Removes the class from the element in order to let the style definitions trigger the image fetching.
359                  * @method _fetchByClass
360                  * @private
361                  */
362                 _fetchByClass: function() {
363                         if (! this._className) {
364                                 return;
365                         }
366
367
368                         Y.all('.' + this._className).removeClass(this._className);
369                 }
370
371         };
372
373
374         Y.extend(Y.ImgLoadGroup, Y.Base, groupProto);
375
376
377         //------------------------------------------------
378
379
380         /**
381          * Image objects to be registered with the groups
382          * @class ImgLoadImgObj
383          * @extends Base
384          * @constructor
385          */
386         Y.ImgLoadImgObj = function() {
387                 Y.ImgLoadImgObj.superclass.constructor.apply(this, arguments);
388                 this._init();
389         };
390                 
391         Y.ImgLoadImgObj.NAME = 'imgLoadImgObj';
392
393         Y.ImgLoadImgObj.ATTRS = {
394                 /**
395                  * HTML DOM id of the image element.
396                  * @attribute domId
397                  * @type String
398                  */
399                 domId: {
400                         value: null,
401                         writeOnce: true
402                 },
403
404                 /**
405                  * Background URL for the image.
406                  * For an image whose URL is specified by "<code>background-image</code>" in the element's style.
407                  * @attribute bgUrl
408                  * @type String
409                  */
410                 bgUrl: {
411                         value: null
412                 },
413
414                 /**
415                  * Source URL for the image.
416                  * For an image whose URL is specified by a "<code>src</code>" attribute in the DOM element.
417                  * @attribute srcUrl
418                  * @type String
419                  */
420                 srcUrl: {
421                         value: null
422                 },
423
424                 /**
425                  * Pixel width of the image. Will be set as a <code>width</code> attribute on the DOM element after the image is fetched.
426                  * Defaults to the natural width of the image (no <code>width</code> attribute will be set).
427                  * Usually only used with src images.
428                  * @attribute width
429                  * @type Int
430                  */
431                 width: {
432                         value: null
433                 },
434
435                 /**
436                  * Pixel height of the image. Will be set as a <code>height</code> attribute on the DOM element after the image is fetched.
437                  * Defaults to the natural height of the image (no <code>height</code> attribute will be set).
438                  * Usually only used with src images.
439                  * @attribute height
440                  * @type Int
441                  */
442                 height: {
443                         value: null
444                 },
445
446                 /**
447                  * Whether the image's <code>style.visibility</code> should be set to <code>visible</code> after the image is fetched.
448                  * Used when setting images as <code>visibility:hidden</code> prior to image fetching.
449                  * @attribute setVisible
450                  * @type Boolean
451                  */
452                 setVisible: {
453                         value: false
454                 },
455
456                 /**
457                  * Whether the image is a PNG.
458                  * PNG images get special treatment in that the URL is specified through AlphaImageLoader for IE, versions 6 and earlier.
459                  * Only used with background images.
460                  * @attribute isPng
461                  * @type Boolean
462                  */
463                 isPng: {
464                         value: false
465                 },
466
467                 /**
468                  * AlphaImageLoader <code>sizingMethod</code> property to be set for the image.
469                  * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
470                  * Defaults to <code>scale</code>.
471                  * @attribute sizingMethod
472                  * @type String
473                  */
474                 sizingMethod: {
475                         value: 'scale'
476                 },
477
478                 /**
479                  * AlphaImageLoader <code>enabled</code> property to be set for the image.
480                  * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
481                  * Defaults to <code>true</code>.
482                  * @attribute enabled
483                  * @type String
484                  */
485                 enabled: {
486                         value: 'true'
487                 }
488
489         };
490
491         var imgProto = {
492
493                 /**
494                  * Initialize all private members needed for the group.
495                  * @method _init
496                  * @private
497                  */
498                 _init: function() {
499
500                         /**
501                          * Whether this image has already been fetched.
502                          * In the case of fold-conditional groups, images won't be fetched twice.
503                          * @property _fetched
504                          * @private
505                          * @type Boolean
506                          */
507                         this._fetched = false;
508
509                         /**
510                          * The Node object returned from <code>Y.one</code>, to avoid repeat calls to access the DOM.
511                          * @property _imgEl
512                          * @private
513                          * @type Object
514                          */
515                         this._imgEl = null;
516
517                         /**
518                          * The vertical position returned from <code>getY</code>, to avoid repeat calls to access the DOM.
519                          * The Y position is checked only for images registered with fold-conditional groups. The position is checked first at page load (domready)
520                          *   and this caching enhancement assumes that the image's vertical position won't change after that first check.
521                          * @property _yPos
522                          * @private
523                          * @type Int
524                          */
525                         this._yPos = null;
526                 },
527
528                 /**
529                  * Displays the image; puts the URL into the DOM.
530                  * This method shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
531                  * @method fetch
532                  * @param {Int} withinY  The pixel distance from the top of the page, for which if the image lies within, it will be fetched. Undefined indicates that no check should be made, and the image should always be fetched
533                  * @return {Boolean}  Whether the image has been fetched (either during this execution or previously)
534                  */
535                 fetch: function(withinY) {
536                         if (this._fetched) {
537                                 return true;
538                         }
539
540                         var el = this._getImgEl(),
541                             yPos;
542                         if (! el) {
543                                 return false;
544                         }
545
546                         if (withinY) {
547                                 // need a distance check
548                                 yPos = this._getYPos();
549                                 if (! yPos || yPos > withinY) {
550                                         return false;
551                                 }
552                         }
553
554
555                         // apply url
556                         if (this.get('bgUrl') !== null) {
557                                 // bg url
558                                 if (this.get('isPng') && Y.UA.ie && Y.UA.ie <= 6) {
559                                         // png for which to apply AlphaImageLoader
560                                         el.setStyle('filter', 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.get('bgUrl') + '", sizingMethod="' + this.get('sizingMethod') + '", enabled="' + this.get('enabled') + '")');
561                                 }
562                                 else {
563                                         // regular bg image
564                                         el.setStyle('backgroundImage', "url('" + this.get('bgUrl') + "')");
565                                 }
566                         }
567                         else if (this.get('srcUrl') !== null) {
568                                 // regular src image
569                                 el.setAttribute('src', this.get('srcUrl'));
570                         }
571
572                         // apply attributes
573                         if (this.get('setVisible')) {
574                                 el.setStyle('visibility', 'visible');
575                         }
576                         if (this.get('width')) {
577                                 el.setAttribute('width', this.get('width'));
578                         }
579                         if (this.get('height')) {
580                                 el.setAttribute('height', this.get('height'));
581                         }
582
583                         this._fetched = true;
584
585                         return true;
586                 },
587
588                 /**
589                  * Gets the object (as a <code>Y.Node</code>) of the DOM element indicated by "<code>domId</code>".
590                  * @method _getImgEl
591                  * @returns {Object} DOM element of the image as a <code>Y.Node</code> object
592                  * @private
593                  */
594                 _getImgEl: function() {
595                         if (this._imgEl === null) {
596                                 this._imgEl = Y.one('#' + this.get('domId'));
597                         }
598                         return this._imgEl;
599                 },
600
601                 /**
602                  * Gets the Y position of the node in page coordinates.
603                  * Expects that the page-coordinate position of the image won't change.
604                  * @method _getYPos
605                  * @returns {Object} The Y position of the image
606                  * @private
607                  */
608                 _getYPos: function() {
609                         if (this._yPos === null) {
610                                 this._yPos = this._getImgEl().getY();
611                         }
612                         return this._yPos;
613                 }
614
615         };
616
617
618         Y.extend(Y.ImgLoadImgObj, Y.Base, imgProto);
619
620
621
622
623 }, '3.3.0' ,{requires:['base-base', 'node-style', 'node-screen']});