OSDN Git Service

Version 0.6.134, add comments for closure compiler.
[pettanr/clientJs.git] / 0.6.x / js / 20_ui / 15_ScrollBox.js
1 \r
2 \r
3 function X_UI_ScrollBox_momentum( current, start, time, lowerMargin, wrapperSize, deceleration ){\r
4         var distance = current - start,\r
5                 speed    = Math.abs( distance ) / time,\r
6                 destination,\r
7                 duration;\r
8 \r
9         deceleration = deceleration === undefined ? 0.0006 : deceleration;\r
10 \r
11         destination  = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );\r
12         duration     = speed / deceleration;\r
13 \r
14         if( destination < lowerMargin ){\r
15                 destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;\r
16                 distance    = Math.abs( destination - current );\r
17                 duration    = distance / speed;\r
18         } else\r
19         if ( destination > 0 ) {\r
20                 destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;\r
21                 distance    = Math.abs( current ) + destination;\r
22                 duration    = distance / speed;\r
23         };\r
24 \r
25         return {\r
26                 destination : Math.round( destination ),\r
27                 duration    : duration\r
28         };\r
29 };\r
30 \r
31 var X_UI_ScrollBox_SUPPORT_ATTRS = {\r
32                 // スクロール開始するために必要な移動距離、縦か横、どちらか制限する場合、より重要\r
33                 directionLockThreshold : [   10, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.LENGTH ],\r
34                 scrollX                : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
35                 scrollY                : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
36                 enabled                : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
37                 bounce                 : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
38                 bounceTime             : [  600, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.TIME ],\r
39                 useWheel               : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
40                 useKey                 : [ true, X.UI.Dirty.CLEAN, X.UI.Attr.USER.UINODE, X.UI.Attr.Type.BOOLEAN ],\r
41         };\r
42 \r
43 \r
44 X.UI._ScrollBox = X.UI._ChromeBox.inherits(\r
45         '_ScrollBox',\r
46         X.Class.PRIVATE_DATA | X.Class.SUPER_ACCESS,\r
47         {\r
48                 //elmScroll     : null,\r
49                 //elmScroller   : null,\r
50                 //elmScrollbar  : null,\r
51                 \r
52                 supportAttrs   : X.UI.Attr.createAttrDef( X.UI.Attr.Support, X_UI_ScrollBox_SUPPORT_ATTRS ),\r
53                 \r
54                 scrolling      : false,\r
55                 \r
56                 initiated       : '',\r
57                 moved               : false,\r
58                 distX               : 0,\r
59                 distY               : 0,\r
60                 directionX      : 0,\r
61                 directionY      : 0,\r
62                 directionLocked : '',\r
63                 startTime       : 0,\r
64                 endTime         : 0,\r
65                 isAnimating     : false,\r
66                 startX          : 0,\r
67                 startY          : 0,\r
68                 absStartX       : 0,\r
69                 absStartY       : 0,\r
70                 pointX          : 0,\r
71                 pointY          : 0,\r
72                 maxScrollX      : 0,\r
73                 maxScrollY      : 0,\r
74                 hasHScroll      : false,\r
75                 hasVScroll      : false,\r
76 \r
77                 wrapperOffset   : 0,\r
78                 wheelTimeout    : 0,\r
79                 requestFrameID  : 0,\r
80                 \r
81                 _scrollX        : 0,\r
82                 _scrollY        : 0,\r
83                 scrollXPercent  : 0,\r
84                 scrollYPercent  : 0,\r
85                 \r
86                 lastScrollWidth  : 0,\r
87                 lastScrollHeight : 0,\r
88                 lastBoxWidth     : 0,\r
89                 lastBoxHeight    : 0,\r
90                 \r
91                 _containerNode : null,\r
92                 scrollManager  : null,\r
93                 \r
94                 Constructor : function( layout, args ){\r
95                         this[ 'Super' ]( layout, args );\r
96                         this._containerNode = X_Class_getPrivate( this.containerNode );\r
97                 },\r
98                 \r
99                 creationComplete : function(){\r
100                         X.UI._Box.prototype.creationComplete.apply( this, arguments );\r
101                         this.scrollManager;\r
102                         this._check();\r
103                 },\r
104                 \r
105                 calculate : function(){\r
106                         this.lastScrollWidth  = this.scrollWidth;\r
107                         this.lastScrollHeight = this.scrollHeight;\r
108                         this.lastBoxWidth     = this.boxWidth;\r
109                         this.lastBoxHeight    = this.boxHeight;\r
110                         \r
111                         X.UI._Box.prototype.calculate.apply( this, arguments );\r
112                         \r
113                         if(\r
114                                         this.lastScrollWidth !== this.scrollWidth || this.lastScrollHeight !== this.scrollHeight ||\r
115                                         this.lastBoxWidth    !== this.boxWidth    || this.lastBoxHeight    !== this.boxHeight\r
116                                 ){\r
117                                         // scroll の停止、GPU の解除\r
118                                         this._check();\r
119                                 };\r
120                 \r
121                 },\r
122                 \r
123                 scrollBy : function( x, y, opt_time, opt_easing ){\r
124                         this.scrollTo( this.x + x, this.y + y, opt_time, opt_easing );\r
125                 },\r
126         \r
127                 scrollTo : function( x, y, opt_time, opt_easing ){\r
128                         opt_time   = 0 <= opt_time ? opt_time : 0;\r
129                         opt_easing = opt_easing || 'circular';\r
130         \r
131                         this.isInTransition = !!opt_time;\r
132         \r
133                         this.containerNode.animate(\r
134                                 {\r
135                                         x : this.x,\r
136                                         y : this.y\r
137                                 },\r
138                                 {\r
139                                         x : x,\r
140                                         y : y\r
141                                 },\r
142                                 opt_time, opt_easing, 1000\r
143                         );\r
144         \r
145                         this.x = x;\r
146                         this.y = y;\r
147         \r
148                         if( this.indicators ){\r
149                                 for( i = this.indicators.length; i--; ){\r
150                                         this.indicators[ i ].updatePosition( opt_time, opt_easing );\r
151                                 };\r
152                         };\r
153                 },\r
154                 \r
155                 _check : function(){\r
156                         var needVScroll, needHScroll;\r
157                         if( this.boxWidth < this._containerNode.scrollWidth || this.boxHeight < this._containerNode.scrollHeight ){\r
158                                 // scroll\r
159                                 if( this.scrolling ){\r
160                                         // fix scroll position from scrollXPercent, scrollYPercent\r
161                                         //\r
162                                         \r
163                                 } else {\r
164                                         // create scroller\r
165                                         \r
166                                         \r
167                                         this[ 'listen' ]( X.UI.Event.POINTER_START, X_UI_ScrollBox_onStart );\r
168                                         \r
169                                         \r
170                                         this._move( 0, 0 );\r
171                                         \r
172                                         this.scrolling = true;\r
173                                 };\r
174                         } else\r
175                         // no scroll\r
176                         if( this.scrolling ){\r
177                                 // remove scroller\r
178                                 this[ 'unlisten' ]( X.UI.Event.POINTER_START );\r
179                                 \r
180                                 ( this._scrollX !== 0 || this._scrollY !== 0 ) && this._move( 0, 0 );\r
181                                 \r
182                                 delete this.scrolling;\r
183                         };\r
184                 },\r
185                 \r
186                 _move : function( x, y ){\r
187                         \r
188                 },\r
189                 \r
190                 _remove : function(){\r
191                         X.UI._AbstractUINode.prototype._remove.apply( this, arguments );\r
192                         if( this.scrolling ){\r
193                                 // remove scroll\r
194                         };\r
195                 }\r
196                 \r
197         }\r
198 );\r
199 \r
200 \r
201 function X_UI_ScrollBox_onStart( e ){\r
202         var ret = X_Callback_NONE;\r
203                                                 \r
204         // React to left mouse button only\r
205         if( e.pointerType === 'mouse' && e.button !== 0 ){\r
206                 return ret;\r
207         };\r
208 \r
209         if( !this.enabled || ( this.initiated && e.pointerType !== this.initiated ) ){\r
210                 return ret;\r
211         };\r
212 \r
213         this.initiated       = e.pointerType;\r
214         this.moved                   = false;\r
215         this.distX                   = 0;\r
216         this.distY                   = 0;\r
217         this.directionX      = 0;\r
218         this.directionY      = 0;\r
219         this.directionLocked = '';\r
220         this.startTime       = X_Timer_now();\r
221 \r
222         // スクロール中の停止\r
223         if( this.isAnimating ){\r
224                 delete this.isAnimating;\r
225                 this[ 'dispatch' ]( X.UI.Event.SCROLL_END );\r
226         };                      \r
227 \r
228         this.startX    = this.x;\r
229         this.startY    = this.y;\r
230         this.absStartX = this.x;\r
231         this.absStartY = this.y;\r
232         this.pointX    = e.pageX;\r
233         this.pointY    = e.pageY;\r
234         \r
235         this[ 'listen' ]( X.UI.Event.POINTER_MOVE, X_UI_ScrollBox_onMove );\r
236         this[ 'listen' ]( X.UI.Event.POINTER_END , X_UI_ScrollBox_onEnd );\r
237 \r
238         //console.log( 'start : 3' );\r
239         return ret | X_Callback_PREVENT_DEFAULT;\r
240 };\r
241 \r
242 function X_UI_ScrollBox_onMove( e ){\r
243         var ret = X_Callback_NONE,\r
244                 deltaX, deltaY, timestamp,\r
245                 newX, newY,\r
246                 absDistX, absDistY;\r
247         // 規定以上の move でスクロール開始\r
248         \r
249         if( !this.enabled || e.pointerType !== this.initiated ){\r
250                 return ret;\r
251         };\r
252 \r
253         // gpu の用意\r
254         if( !this.containerNode[ '_anime' ] ){\r
255                 console.log( 'gpuレイヤーの用意' );\r
256                 this._translate( this.x, this.y );\r
257                 return ret;\r
258         };\r
259 \r
260         deltaX          = e.pageX - this.pointX;\r
261         deltaY          = e.pageY - this.pointY;\r
262         timestamp       = X_Timer_now();\r
263 \r
264         this.pointX     = e.pageX;\r
265         this.pointY     = e.pageY;\r
266 \r
267         this.distX      += deltaX;\r
268         this.distY      += deltaY;\r
269         absDistX        = Math.abs(this.distX);\r
270         absDistY        = Math.abs(this.distY);\r
271         \r
272         // We need to move at least 10 pixels for the scrolling to initiate\r
273         if( 300 < timestamp - this.endTime && ( absDistX < 10 && absDistY < 10 ) ){\r
274                 return ret;\r
275         };\r
276 \r
277         // If you are scrolling in one direction lock the other\r
278         if( !this.directionLocked ){\r
279                 if( absDistX > absDistY + this.directionLockThreshold ){\r
280                         this.directionLocked = 'h';             // lock horizontally\r
281                 } else\r
282                 if( absDistY >= absDistX + this.directionLockThreshold ){\r
283                         this.directionLocked = 'v';             // lock vertically\r
284                 } else {\r
285                         this.directionLocked = 'n';             // no lock\r
286                 };\r
287         };\r
288 \r
289         if( this.directionLocked === 'h' ){\r
290                 deltaY = 0;\r
291         } else\r
292         if( this.directionLocked === 'v' ){\r
293                 deltaX = 0;\r
294         };\r
295 \r
296         deltaX = this.hasHScroll ? deltaX : 0;\r
297         deltaY = this.hasVScroll ? deltaY : 0;\r
298 \r
299         if( !this.moved ){\r
300                 this[ 'dispatch' ]( X.UI.Event.SCROLL_BEFORE_MOVE );\r
301                 this.moved  = true;\r
302                 this.minusX = deltaX;\r
303                 this.minusY = deltaY;\r
304         } else {\r
305                 this[ 'dispatch' ]( X.UI.Event.SCROLL_MOVE );\r
306         };\r
307 \r
308         newX = this.x + deltaX;// - this.minusX;\r
309         newY = this.y + deltaY;// - this.minusY;\r
310 \r
311         // Slow down if outside of the boundaries\r
312         if( 0 < newX || newX < this.maxScrollX ){\r
313                 newX = this.bounce ? this.x + ( deltaX ) / 3 : 0 < newX ? 0 : this.maxScrollX;\r
314         };\r
315         if( 0 < newY || newY < this.maxScrollY ){\r
316                 newY = this.bounce ? this.y + ( deltaY ) / 3 : 0 < newY ? 0 : this.maxScrollY;\r
317         };\r
318 \r
319         this.directionX = 0 < deltaX ? -1 : deltaX < 0 ? 1 : 0;\r
320         this.directionY = 0 < deltaY ? -1 : deltaY < 0 ? 1 : 0;\r
321 \r
322         this._translate( newX, newY );\r
323 \r
324         if( 300 < timestamp - this.startTime ){\r
325                 this.startTime = timestamp;\r
326                 this.startX = this.x;\r
327                 this.startY = this.y;\r
328         };\r
329         // イベントの拘束\r
330         return ret | X_Callback_PREVENT_DEFAULT | X_Callback_MONOPOLY;\r
331 };\r
332 \r
333 function X_UI_ScrollBox_onEnd( e ){\r
334         var ret    = X_Callback_NONE,\r
335                 time   = 0,\r
336                 easing = '',\r
337                 newX, newY,\r
338                 momentumX, momentumY,\r
339                 duration, distanceX, distanceY;\r
340                                                 \r
341         this[ 'unlisten' ]( X.UI.Event.POINTER_MOVE, X_UI_ScrollBox_onMove );\r
342         this[ 'unlisten' ]( X.UI.Event.POINTER_END,  X_UI_ScrollBox_onEnd );\r
343         \r
344         if( !this.enabled || e.pointerType !== this.initiated ){\r
345                 return ret;\r
346         };\r
347 \r
348         delete this.isInTransition;\r
349         delete this.initiated;\r
350         this.endTime = X_Timer_now();                   \r
351 \r
352         duration  = this.endTime - this.startTime;\r
353         newX      = Math.round( this.x );\r
354         newY      = Math.round( this.y );\r
355         distanceX = Math.abs(newX - this.startX);\r
356         distanceY = Math.abs(newY - this.startY);\r
357 \r
358         // reset if we are outside of the boundaries\r
359         if( X_UI_ScrollBox_resetPosition( this, this.options.bounceTime ) ){\r
360                 return ret;\r
361         };\r
362 \r
363         // we scrolled less than 10 pixels\r
364         if( !this.moved ){\r
365                 // this[ 'dispatch' ]( X_EVENT_CANCELED );\r
366                 return ret;\r
367         };\r
368         \r
369         this.scrollTo( newX, newY, 0 ); // ensures that the last position is rounded\r
370 \r
371         // start momentum animation if needed\r
372         if( this.options.momentum && duration < 300 ){\r
373                 momentumX = this.hasHScroll ?\r
374                                                 X_UI_ScrollBox_momentum( this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth  : 0, this.options.deceleration ) :{ destination: newX, duration: 0 };\r
375                 momentumY = this.hasVScroll   ?\r
376                                                 X_UI_ScrollBox_momentum( this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration ) : { destination: newY, duration: 0 };\r
377                 newX = momentumX.destination;\r
378                 newY = momentumY.destination;\r
379                 time = Math.max( momentumX.duration, momentumY.duration );\r
380                 this.isInTransition = 1;\r
381         };\r
382 \r
383         if( newX != this.x || newY != this.y ){\r
384                 // change easing function when scroller goes out of the boundaries\r
385                 if( 0 < newX || newX < this.maxScrollX || 0 < newY || newY < this.maxScrollY ){\r
386                         easing = 'quadratic';\r
387                 };\r
388 \r
389                 this.scrollTo( newX, newY, time, easing );\r
390                 return ret;\r
391         };\r
392 \r
393         this[ 'dispatch' ]( X.UI.Event.SCROLL_END );\r
394         \r
395         return ret;\r
396 };\r
397 \r
398 function X_UI_ScrollBox_resetPosition( that, time ){\r
399         var x = this.x,\r
400                 y = this.y;\r
401 \r
402         time = time || 0;\r
403 \r
404         if( !this.hasHScroll || 0 < this.x ){\r
405                 x = 0;\r
406         } else\r
407         if( this.x < this.maxScrollX ){\r
408                 x = this.maxScrollX;\r
409         };\r
410 \r
411         if( !this.hasVScroll || 0 < this.y ){\r
412                 y = 0;\r
413         } else\r
414         if( this.y < this.maxScrollY ){\r
415                 y = this.maxScrollY;\r
416         };\r
417 \r
418         if( x === this.x && y === this.y ){\r
419                 console.log( 'no バウンド' );\r
420                 return false;\r
421         };\r
422 \r
423         console.log( 'バウンド!' );\r
424         this.scrollTo( x, y, time, this.options.bounceEasing );\r
425 \r
426         return true;\r
427 };\r
428 \r
429 function X_UI_ScrollBox_translate( x, y ){\r
430         this.containerNode.animate(\r
431                 {\r
432                         x : this.x,\r
433                         y : this.y\r
434                 },\r
435                 {\r
436                         x : x,\r
437                         y : y\r
438                 },\r
439                 0, '', 300\r
440         );\r
441         \r
442         this.x = x;\r
443         this.y = y;\r
444         \r
445         if( this.indicators ){\r
446                 for( i = this.indicators.length; i--; ){\r
447                         this.indicators[ i ].updatePosition();\r
448                 };\r
449         };\r
450 };\r
451 \r
452 function X_UI_ScrollBox_refresh( remove ){\r
453         this.maxScrollX = this.boxWidth  - this.containerNode.boxWidth;\r
454         this.maxScrollY = this.boxHeight - this.containerNode.boxHeight;\r
455 \r
456         this.hasHScroll = this.User.attr( 'scrollX' ) && this.maxScrollX < 0;\r
457         this.hasVScroll = this.User.attr( 'scrollY' ) && this.maxScrollY < 0;\r
458 \r
459         if( !this.hasHScroll ){\r
460                 this.maxScrollX    = 0;\r
461                 this.scrollerWidth = this.wrapperWidth;\r
462         };\r
463 \r
464         if( !this.hasVScroll ){\r
465                 this.maxScrollY     = 0;\r
466                 this.scrollerHeight = this.wrapperHeight;\r
467         };\r
468 \r
469         delete this.endTime;\r
470         delete this.directionX;\r
471         delete this.directionY;\r
472 \r
473         this.wrapperOffset = this.xnodeWrapper[ 'offset' ]();\r
474 \r
475         //this[ 'dispatch' ]('refresh');\r
476 \r
477         X_UI_ScrollBox_resetPosition( this, 0 );\r
478 };\r
479 \r
480 X.UI.ScrollBox = X.UI.ChromeBox.inherits(\r
481         'ScrollBox',\r
482         X.Class.SUPER_ACCESS,\r
483         X.UI._ScrollBox,\r
484         {\r
485                 Constructor : function(){\r
486                         var args = [],\r
487                                 i    = arguments.length,\r
488                                 arg, layout;\r
489                         \r
490                         for( ; i; ){\r
491                                 arg = arguments[ --i ];\r
492                                 if( arg[ 'instanceOf' ] && arg[ 'instanceOf' ]( X.UI.Layout.Base ) ){\r
493                                         layout = arg;\r
494                                 } else {\r
495                                         args[ args.length ] = arg;\r
496                                 };\r
497                         };\r
498                         /*\r
499                         this.style = DisplayNodeStyle( this,\r
500                                 X_Class_newPrivate(\r
501                                         this,\r
502                                         X.UI.Layout.Canvas,\r
503                                         [\r
504                                                 Box(\r
505                                                         layout || VerticalLayoutManager,\r
506                                                         {\r
507                                                                 name : 'ScrollBox-Scroller',\r
508                                                                 role : 'container'\r
509                                                         },\r
510                                                         args\r
511                                                 )\r
512                                         ]\r
513                                 )\r
514                         );\r
515                         this.style.addName( 'ScrollBox' ); */\r
516                 },\r
517                 scrollX  : function(){\r
518                         \r
519                 },\r
520                 scrollY  : function(){\r
521                         \r
522                 },\r
523                 scrollWidth : function(){\r
524                         \r
525                 },\r
526                 scrollHeight : function(){\r
527                         \r
528                 },\r
529                 scrollTo : function( nodeOrX, y ){\r
530                         \r
531                 }\r
532         }\r
533 );