]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/jquery/jquery.hoverscroll.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / jquery / jquery.hoverscroll.js
1 /**
2  * HoverScroll jQuery Plugin
3  *
4  * Make an unordered list scrollable by hovering the mouse over it
5  *
6  * @author RasCarlito <carl.ogren@gmail.com>
7  * @version 0.2.4
8  * @revision 21
9  *
10  * 
11  *
12  * FREE BEER LICENSE VERSION 1.02
13  *
14  * The free beer license is a license to give free software to you and free
15  * beer (in)to the author(s).
16  * 
17  *
18  * Released: 09-12-2010 11:31pm
19  *
20  * Changelog
21  * ----------------------------------------------------
22  *
23  * 0.2.4    - Added "Right to Left" option, only works with horizontal lists
24  *          - Optimization of arrows opacity control (Thanks to Josef Körner)
25  *
26  * 0.2.3    - Added fixed arrows option
27  *          - Binded startMoving and stopMoving functions to the HoverScrolls HTML object for external access
28  *
29  * 0.2.2    Bug fixes
30  *          - Backward compatibility with jQuery 1.1.x
31  *          - Added test file to the archive
32  *          - Bug fix: The right arrow appeared when it wasn't necessary (thanks to <admin at unix dot am>)
33  *        
34  * 0.2.1    Bug fixes
35  *          - Backward compatibility with jQuery 1.2.x (thanks to Andy Mull for compatibility with jQuery >= 1.2.4)
36  *          - Added information to the debug log
37  * 
38  * 0.2.0    Added some new features
39  *          - Direction indicator arrows
40  *          - Permanent override of default parameters
41  * 
42  * 0.1.1    Minor bug fix
43  *          - Hover zones did not cover the complete container
44  *
45  *          note: The css file has not changed therefore it is still versioned 0.1.0
46  *
47  * 0.1.0    First release of the plugin. Supports:
48  *          - Horizontal and vertical lists
49  *          - Width and height control
50  *          - Debug log (doesn't show useful information for the moment)
51  */
52  
53 (function($) {
54
55 /**
56  * @method hoverscroll
57  * @param       params {Object}  Parameter list
58  *      params = {
59  *              vertical {Boolean},     // Vertical list or not ?
60  *              width {Integer},        // Width of list container
61  *              height {Integer},       // Height of list container
62  *      arrows {Boolean},       // Show direction indicators or not
63  *      arrowsOpacity {Float},  // Arrows maximum opacity
64  *      fixedArrows {Boolean},  // Display arrows permenantly, this overrides arrowsOpacity option
65  *              debug {Boolean}         // Debug output in firebug console
66  *      };
67  */
68 $.fn.hoverscroll = function(params) {
69         if (!params) { params = {}; }
70         
71         // Extend default parameters
72         // note: empty object to prevent params object from overriding default params object
73         params = $.extend({}, $.fn.hoverscroll.params, params);
74         
75         // Loop through all the elements
76         this.each(function() {
77                 var $this = $(this);
78                 
79                 if (params.debug) {
80                         $.log('[HoverScroll] Trying to create hoverscroll on element ' + this.tagName + '#' + this.id);
81                 }
82                 
83                 // wrap ul list with a div.listcontainer
84         if (params.fixedArrows) {
85             $this.wrap('<div class="fixed-listcontainer"></div>')
86         }
87         else {
88             $this.wrap('<div class="listcontainer"></div>');
89         }
90                 
91                 $this.addClass('listitem');
92                 //.addClass('ui-helper-clearfix');
93                 
94                 // store handle to listcontainer
95                 var listctnr = $this.parent();
96                 
97                 // wrap listcontainer with a div.hoverscroll
98                 listctnr.wrap('<div class="ui-widget-content hoverscroll' +
99                         (params.rtl && !params.vertical ? " rtl" : "") + '"></div>');
100                 //listctnr.wrap('<div class="hoverscroll"></div>');
101                 
102                 // store hoverscroll container
103                 var ctnr = listctnr.parent();
104
105         var leftArrow, rightArrow, topArrow, bottomArrow;
106
107                 // Add arrow containers
108                 if (params.arrows) {
109                         if (!params.vertical) {
110                 if (params.fixedArrows) {
111                     leftArrow = '<div class="fixed-arrow left"></div>';
112                     rightArrow = '<div class="fixed-arrow right"></div>';
113
114                     listctnr.before(leftArrow).after(rightArrow);
115                 }
116                 else {
117                     leftArrow = '<div class="arrow left"></div>';
118                     rightArrow = '<div class="arrow right"></div>';
119                     
120                     listctnr.append(leftArrow).append(rightArrow);
121                 }
122                         }
123                         else {
124                 if (params.fixedArrows) {
125                     topArrow = '<div class="fixed-arrow top"></div>';
126                     bottomArrow = '<div class="fixed-arrow bottom"></div>';
127
128                     listctnr.before(topArrow).after(bottomArrow);
129                 }
130                 else {
131                     topArrow = '<div class="arrow top"></div>';
132                     bottomArrow = '<div class="arrow bottom"></div>';
133
134                     listctnr.append(topArrow).append(bottomArrow);
135                 }
136                         }
137                 }
138                 
139                 // Apply parameters width and height
140                 ctnr.width(params.width).height(params.height);
141
142         if (params.arrows && params.fixedArrows) {
143             if (params.vertical) {
144                 topArrow = listctnr.prev();
145                 bottomArrow = listctnr.next();
146
147                 listctnr.width(params.width)
148                     .height(params.height - (topArrow.height() + bottomArrow.height()));
149             }
150             else {
151                 leftArrow = listctnr.prev();
152                 rightArrow = listctnr.next();
153                 
154                 listctnr.height(params.height)
155                     .width(params.width - (leftArrow.width() + rightArrow.width()));
156             }
157         }
158         else {
159             listctnr.width(params.width).height(params.height);
160         }
161                 
162                 var size = 0;
163                 
164                 if (!params.vertical) {
165                         ctnr.addClass('horizontal');
166                         
167                         // Determine content width
168                         $this.children().each(function() {
169                                 $(this).addClass('item');
170                                 
171                                 if ($(this).outerWidth) {
172                                         size += $(this).outerWidth(true);
173                                 }
174                                 else {
175                                         // jQuery < 1.2.x backward compatibility patch
176                                         size += $(this).width() + parseInt($(this).css('padding-left')) + parseInt($(this).css('padding-right'))
177                                                 + parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
178                                 }
179                         });
180                         // Apply computed width to listcontainer
181                         $this.width(size);
182                         
183                         if (params.debug) {
184                                 $.log('[HoverScroll] Computed content width : ' + size + 'px');
185                         }
186                         
187                         // Retrieve container width instead of using the given params.width to include padding
188                         if (ctnr.outerWidth) {
189                                 size = ctnr.outerWidth();
190                         }
191                         else {
192                                 // jQuery < 1.2.x backward compatibility patch
193                                 size = ctnr.width() + parseInt(ctnr.css('padding-left')) + parseInt(ctnr.css('padding-right'))
194                                         + parseInt(ctnr.css('margin-left')) + parseInt(ctnr.css('margin-right'));
195                         }
196                         
197                         if (params.debug) {
198                                 $.log('[HoverScroll] Computed container width : ' + size + 'px');
199                         }
200                 }
201                 else {
202                         ctnr.addClass('vertical');
203                         
204                         // Determine content height
205                         $this.children().each(function() {
206                                 $(this).addClass('item')
207                                 if ($(this).css('display') != "none"){
208                                         if ($(this).outerHeight) {
209                                                 size += $(this).outerHeight(true);
210                                         }
211                                         else {
212                                                 // jQuery < 1.2.x backward compatibility patch
213                                                 size += $(this).height() + parseInt($(this).css('padding-top')) + parseInt($(this).css('padding-bottom'))
214                                                         + parseInt($(this).css('margin-bottom')) + parseInt($(this).css('margin-bottom'));
215                                         }
216                                 }
217                         });
218                         // Apply computed height to listcontainer
219                         $this.height(size);
220                         
221                         if (params.debug) {
222                                 $.log('[HoverScroll] Computed content height : ' + size + 'px');
223                         }
224                         
225                         // Retrieve container height instead of using the given params.height to include padding
226                         if (ctnr.outerHeight) {
227                                 size = ctnr.outerHeight();
228                         }
229                         else {
230                                 // jQuery < 1.2.x backward compatibility patch
231                                 size = ctnr.height() + parseInt(ctnr.css('padding-top')) + parseInt(ctnr.css('padding-bottom'))
232                                         + parseInt(ctnr.css('margin-top')) + parseInt(ctnr.css('margin-bottom'));
233                         }
234                         
235                         if (params.debug) {
236                                 $.log('[HoverScroll] Computed container height : ' + size + 'px');
237                         }
238                 }
239                 
240                 // Define hover zones on container
241                 var arrowHeight = $(this).parent().find('.arrow.top').height();
242                 
243                 
244                 if(params.hoverZone == "gradual") {
245                         var zone = {
246                                 1: {action: 'move', from: 0, to: 0.06 * size, direction: -1 , speed: 16},
247                                 2: {action: 'move', from: 0.06 * size, to: 0.15 * size, direction: -1 , speed: 8},
248                                 3: {action: 'move', from: 0.15 * size, to: 0.25 * size, direction: -1 , speed: 4},
249                                 4: {action: 'move', from: 0.25 * size, to: 0.4 * size, direction: -1 , speed: 2},
250                                 5: {action: 'stop', from: 0.4 * size, to: 0.6 * size},
251                                 6: {action: 'move', from: 0.6 * size, to: 0.75 * size, direction: 1 , speed: 2},
252                                 7: {action: 'move', from: 0.75 * size, to: 0.85 * size, direction: 1 , speed: 4},
253                                 8: {action: 'move', from: 0.85 * size, to: 0.94 * size, direction: 1 , speed: 8},
254                                 9: {action: 'move', from: 0.94 * size, to: size, direction: 1 , speed: 16}
255                         }
256                 } else {
257                         var zone = {
258                                 1: {action: 'move', from: 0, to: arrowHeight, direction: -1 , speed: 16},
259                                 2: {action: 'move', from: size-arrowHeight, to: size, direction: 1 , speed: 16}
260                         }
261                 }
262                 
263                 // Store default state values in container
264                 ctnr[0].isChanging = false;
265                 ctnr[0].direction  = 0;
266                 ctnr[0].speed      = 1;
267                 
268                 
269                 /**
270                  * Check mouse position relative to hoverscroll container
271                  * and trigger actions according to the zone table
272                  *
273                  * @param x {Integer} Mouse X event position
274                  * @param y {Integer} Mouse Y event position
275                  */
276                 function checkMouse(x, y) {
277                         x = x - ctnr.offset().left;
278                         y = y - ctnr.offset().top;
279                         
280                         var pos;
281                         if (!params.vertical) {pos = x;}
282                         else {pos = y;}
283                         
284                         for (i in zone) {
285                                 if (pos >= zone[i].from && pos < zone[i].to) {
286                                         if (zone[i].action == 'move') {startMoving(zone[i].direction, zone[i].speed);}
287                                         else {stopMoving();}
288                                 }
289                         }
290                 }
291                 
292                 
293                 /**
294                  * Sets the opacity of the left|top and right|bottom
295                  * arrows according to the scroll position.
296                  */
297                 function setArrowOpacity() {
298                         if (!params.arrows || params.fixedArrows) {return;}
299                         
300                         var maxScroll;
301                         var scroll;
302                         
303                         if (!params.vertical) {
304                                 maxScroll = listctnr[0].scrollWidth - listctnr.width();
305                                 scroll = listctnr[0].scrollLeft;
306                         }
307                         else {
308                                 maxScroll = listctnr[0].scrollHeight - listctnr.height();
309                                 scroll = listctnr[0].scrollTop;
310                         }
311                         var limit = params.arrowsOpacity;
312                         
313             // Optimization of opacity control by Josef Körner
314             // Initialize opacity; keep it between its extremas (0 and limit) we don't need to check limits after init
315                         var opacity = (scroll / maxScroll) * limit;
316             
317                     if (opacity > limit) { opacity = limit; }
318                         if (isNaN(opacity)) { opacity = 0; }
319             
320                         // Check if the arrows are needed
321                         // Thanks to <admin at unix dot am> for fixing the bug that displayed the right arrow when it was not needed
322                         var done = false;
323                         if (opacity <= 0) {
324                 $('div.arrow.left, div.arrow.top', ctnr).hide();
325                 if(maxScroll > 0) {
326                     $('div.arrow.right, div.arrow.bottom', ctnr).show().css('opacity', limit);
327                 }
328                 done = true;
329             }
330                         if (opacity >= limit || maxScroll <= 0) {
331                     $('div.arrow.right, div.arrow.bottom', ctnr).hide();
332                 done = true;
333             }
334
335                         if (!done) {
336                                 $('div.arrow.left, div.arrow.top', ctnr).show().css('opacity', opacity);
337                                 $('div.arrow.right, div.arrow.bottom', ctnr).show().css('opacity', (limit - opacity));
338                         }
339             // End of optimization
340                 }
341                 
342                 
343                 /**
344                  * Start scrolling the list with a given speed and direction
345                  *
346                  * @param direction {Integer}   Direction of the displacement, either -1|1
347                  * @param speed {Integer}               Speed of the displacement (20 being very fast)
348                  */
349                 function startMoving(direction, speed) {
350                         if (ctnr[0].direction != direction) {
351                                 if (params.debug) {
352                                         $.log('[HoverScroll] Starting to move. direction: ' + direction + ', speed: ' + speed);
353                                 }
354                                 
355                                 stopMoving();
356                                 ctnr[0].direction  = direction;
357                                 ctnr[0].isChanging = true;
358                                 move();
359                         }
360                         if (ctnr[0].speed != speed) {
361                                 if (params.debug) {
362                                         $.log('[HoverScroll] Changed speed: ' + speed);
363                                 }
364                                 
365                                 ctnr[0].speed = speed;
366                         }
367                 }
368                 
369                 /**
370                  * Stop scrolling the list
371                  */
372                 function stopMoving() {
373                         if (ctnr[0].isChanging) {
374                                 if (params.debug) {
375                                         $.log('[HoverScroll] Stoped moving');
376                                 }
377                                 
378                                 ctnr[0].isChanging = false;
379                                 ctnr[0].direction  = 0;
380                                 ctnr[0].speed      = 1;
381                                 clearTimeout(ctnr[0].timer);
382                         }
383                 }
384                 
385                 /**
386                  * Move the list one step in the given direction and speed
387                  */
388                 function move() {
389                         if (ctnr[0].isChanging == false) {return;}
390                         
391                         setArrowOpacity();
392                         
393                         var scrollSide;
394                         if (!params.vertical) {scrollSide = 'scrollLeft';}
395                         else {scrollSide = 'scrollTop';}
396                         
397                         listctnr[0][scrollSide] += ctnr[0].direction * ctnr[0].speed;
398                         ctnr[0].timer = setTimeout(function() {move();}, 50);
399                 }
400
401                 // Initialize "right to left" option if specified
402                 if (params.rtl && !params.vertical) {
403                         listctnr[0].scrollLeft = listctnr[0].scrollWidth - listctnr.width();
404                 }
405                 
406                 // Bind actions to the hoverscroll container
407                 ctnr
408                 // Bind checkMouse to the mousemove
409                 .mousemove(function(e) {checkMouse(e.pageX, e.pageY);})
410                 // Bind stopMoving to the mouseleave
411                 // jQuery 1.2.x backward compatibility, thanks to Andy Mull!
412                 // replaced .mouseleave(...) with .bind('mouseleave', ...)
413                 .bind('mouseleave, mouseout', function() {stopMoving();});
414
415         // Bind the startMoving and stopMoving functions
416         // to the HTML object for external access
417         this.startMoving = startMoving;
418         this.stopMoving = stopMoving;
419                 
420                 if (params.arrows && !params.fixedArrows) {
421                         // Initialise arrow opacity
422                         setArrowOpacity();
423                 }
424                 else {
425                         // Hide arrows
426                         $('.arrowleft, .arrowright, .arrowtop, .arrowbottom', ctnr).hide();
427                 }
428         });
429         
430         return this;
431 };
432
433
434 // Backward compatibility with jQuery 1.1.x
435 if (!$.fn.offset) {
436         $.fn.offset = function() {
437                 this.left = this.top = 0;
438                 
439                 if (this[0] && this[0].offsetParent) {
440                         var obj = this[0];
441                         do {
442                                 this.left += obj.offsetLeft;
443                                 this.top += obj.offsetTop;
444                         } while (obj = obj.offsetParent);
445                 }
446                 
447                 return this;
448         }
449 }
450
451
452
453 /**
454  * HoverScroll default parameters
455  */
456 $.fn.hoverscroll.params = {
457         vertical:       false,      // Display the list vertically or not
458         width:          400,        // Width of the list
459         height:         50,         // Height of the list
460         arrows:         true,       // Display arrows to the left and top or the top and bottom
461         arrowsOpacity:  0.7,    // Maximum opacity of the arrows if fixedArrows
462     fixedArrows: false,     // Fix the displayed arrows to the side of the list
463         rtl:            false,          // Set display mode to "Right to Left"
464         debug:          false,       // Display some debugging information in firebug console
465         hoverZone:      'gradual'       // Display some debugging information in firebug console
466 };
467
468
469 $.fn.hoverscroll.destroy = function(el) {
470         var container = el.parent().parent(),
471                 originalContainer = container.parent();
472                 
473                 $(el).prependTo(originalContainer)
474                 .removeClass('listitem')
475                 .removeAttr("style");
476                 
477                 container.remove();
478                 
479         //console.log(el.parent().parent());
480 };
481
482 /**
483  * Log errors to consoles (firebug, opera) if exist, else uses alert()
484  */
485 $.log = function() {
486         try {console.log.apply(console, arguments);}
487         catch (e) {
488                 try {opera.postError.apply(opera, arguments);}
489                 catch (e) {
490 //            alert(Array.prototype.join.call(arguments, " "));
491         }
492         }
493 };
494
495
496 })(jQuery);