Merge branch 'lanceui' into mergeui
[bookreader.git] / BookReader / dragscrollable.js
1 /*
2  * jQuery dragscrollable Plugin
3  * version: 1.0 (25-Jun-2009)
4  * Copyright (c) 2009 Miquel Herrera
5  *
6  * Portions Copyright (c) 2010 Reg Braithwaite
7  *          Copyright (c) 2010 Internet Archive / Michael Ang
8  *
9  * Dual licensed under the MIT and GPL licenses:
10  *   http://www.opensource.org/licenses/mit-license.php
11  *   http://www.gnu.org/licenses/gpl.html
12  *
13  */
14 ;(function($){ // secure $ jQuery alias
15
16 /**
17  * Adds the ability to manage elements scroll by dragging
18  * one or more of its descendant elements. Options parameter
19  * allow to specifically select which inner elements will
20  * respond to the drag events.
21  * 
22  * options properties:
23  * ------------------------------------------------------------------------             
24  *  dragSelector         | jquery selector to apply to each wrapped element 
25  *                       | to find which will be the dragging elements. 
26  *                       | Defaults to '>:first' which is the first child of 
27  *                       | scrollable element
28  * ------------------------------------------------------------------------             
29  *  acceptPropagatedEvent| Will the dragging element accept propagated 
30  *                           | events? default is yes, a propagated mouse event 
31  *                           | on a inner element will be accepted and processed.
32  *                           | If set to false, only events originated on the
33  *                           | draggable elements will be processed.
34  * ------------------------------------------------------------------------
35  *  preventDefault       | Prevents the event to propagate further effectivey
36  *                       | dissabling other default actions. Defaults to true
37  * ------------------------------------------------------------------------
38  *  
39  *  usage examples:
40  *
41  *  To add the scroll by drag to the element id=viewport when dragging its 
42  *  first child accepting any propagated events
43  *      $('#viewport').dragscrollable(); 
44  *
45  *  To add the scroll by drag ability to any element div of class viewport
46  *  when dragging its first descendant of class dragMe responding only to
47  *  evcents originated on the '.dragMe' elements.
48  *      $('div.viewport').dragscrollable({dragSelector:'.dragMe:first',
49  *                                                                        acceptPropagatedEvent: false});
50  *
51  *  Notice that some 'viewports' could be nested within others but events
52  *  would not interfere as acceptPropagatedEvent is set to false.
53  *              
54  */
55  
56 var append_namespace = function (string_of_events, ns) {
57     
58     /* IE doesn't have map
59         return string_of_events
60                 .split(' ')
61                         .map(function (name) { return name + ns; })
62                                 .join(' ');
63     */
64     var pieces = string_of_events.split(' ');
65     var ret = new Array();
66     for (var i = 0; i < pieces.length; i++) {
67         ret.push(pieces[i] + ns);
68     }
69     return ret.join(' ');
70 };
71
72 var left_top = function(event) {
73         
74         var x;
75         var y;
76         if (typeof(event.clientX) != 'undefined') {
77                 x = event.clientX;
78                 y = event.clientY;
79         }
80         else if (typeof(event.screenX) != 'undefined') {
81                 x = event.screenX;
82                 y = event.screenY;
83         }
84         else if (typeof(event.targetTouches) != 'undefined') {
85                 x = event.targetTouches[0].pageX;
86                 y = event.targetTouches[0].pageY;
87         }
88         else if (typeof(event.originalEvent) == 'undefined') {
89                 var str = '';
90                 for (i in event) {
91                         str += ', ' + i + ': ' + event[i];
92                 }
93                 console.error("don't understand x and y for " + event.type + ' event: ' + str);
94         }
95         else if (typeof(event.originalEvent.clientX) != 'undefined') {
96                 x = event.originalEvent.clientX;
97                 y = event.originalEvent.clientY;
98         }
99         else if (typeof(event.originalEvent.screenX) != 'undefined') {
100                 x = event.originalEvent.screenX;
101                 y = event.originalEvent.screenY;
102         }
103         else if (typeof(event.originalEvent.targetTouches) != 'undefined') {
104                 x = event.originalEvent.targetTouches[0].pageX;
105                 y = event.originalEvent.targetTouches[0].pageY;
106         }
107         
108         return {left: x, top:y};
109 };
110
111 $.fn.dragscrollable = function( options ) {
112         
113         var handling_element = $(this);
114    
115         var settings = $.extend(
116                 {   
117                         dragSelector:'>:first',
118                         acceptPropagatedEvent: true,
119             preventDefault: true,
120                         dragstart: 'mousedown touchstart',
121                         dragcontinue: 'mousemove touchmove',
122                         dragend: 'mouseup mouseleave touchend',
123                         dragMinDistance: 5,
124                         namespace: '.ds'
125                 },options || {});
126         
127         settings.dragstart = append_namespace(settings.dragstart, settings.namespace);
128         settings.dragcontinue = append_namespace(settings.dragcontinue, settings.namespace);
129         settings.dragend = append_namespace(settings.dragend, settings.namespace);
130
131         var dragscroll= {
132                 dragStartHandler : function(event) {
133                         
134                         // mousedown, left click, check propagation
135                         if (event.which > 1 ||
136                                 (!event.data.acceptPropagatedEvent && event.target != this)){ 
137                                 return false; 
138                         }
139                         
140                         event.data.firstCoord = left_top(event);
141                         // Initial coordinates will be the last when dragging
142                         event.data.lastCoord = event.data.firstCoord;
143                         
144                         handling_element
145                                 .bind(settings.dragcontinue, event.data, dragscroll.dragContinueHandler)
146                                 .bind(settings.dragend, event.data, dragscroll.dragEndHandler);
147                 
148                         if (event.data.preventDefault) {
149                 event.preventDefault();
150                 return false;
151             }
152                 },
153                 dragContinueHandler : function(event) { // User is dragging
154                         
155                         var lt = left_top(event);
156                         
157                         // How much did the mouse move?
158                         var delta = {left: (lt.left - event.data.lastCoord.left),
159                                                  top: (lt.top - event.data.lastCoord.top)};
160                         
161                         // Set the scroll position relative to what ever the scroll is now
162                         event.data.scrollable.scrollLeft(
163                                                         event.data.scrollable.scrollLeft() - delta.left);
164                         event.data.scrollable.scrollTop(
165                                                         event.data.scrollable.scrollTop() - delta.top);
166                         
167                         // Save where the cursor is
168                         event.data.lastCoord = lt;
169                         
170                         if (event.data.preventDefault) {
171                 event.preventDefault();
172                 return false;
173             }
174
175                 },
176                 dragEndHandler : function(event) { // Stop scrolling
177                 
178                         handling_element
179                                 .unbind(settings.dragcontinue)
180                                 .unbind(settings.dragend);
181                                 
182                         // How much did the mouse move total?
183                         var delta = {left: Math.abs(event.data.lastCoord.left - event.data.firstCoord.left),
184                                                  top: Math.abs(event.data.lastCoord.top - event.data.firstCoord.top)};
185                         var distance = Math.max(delta.left, delta.top);
186                                                 
187                         // Trigger 'tap' if did not meet drag distance
188                         // $$$ does not differentiate single vs multi-touch
189                         if (distance < settings.dragMinDistance) {
190                             //$(event.originalEvent.target).trigger('tap');
191                             $(event.target).trigger('tap'); // $$$ always the right target?
192                         }
193                         
194                         // Allow event to propage if min distance was not achieved
195                         if (event.data.preventDefault && distance > settings.dragMinDistance) {
196                 event.preventDefault();
197                 return false;
198             }
199                 }
200         }
201         
202         // set up the initial events
203         return this.each(function() {
204                 // closure object data for each scrollable element
205                 var data = {scrollable : $(this),
206                                         acceptPropagatedEvent : settings.acceptPropagatedEvent,
207                     preventDefault : settings.preventDefault }
208                 // Set mouse initiating event on the desired descendant
209                 $(this).find(settings.dragSelector).
210                                                 bind(settings.dragstart, data, dragscroll.dragStartHandler);
211         });
212 }; //end plugin dragscrollable
213
214 $.fn.removedragscrollable = function (namespace) {
215         if (typeof(namespace) == 'undefined')
216                 namespace = '.ds';
217         return this.each(function() {
218                 var x = $(document).find('*').andSelf().unbind(namespace);
219         });
220 };
221
222 })( jQuery ); // confine scope