gallery3d: Add new widget
[platform/framework/web/web-ui-fw.git] / src / js / widgets / jquery.mobile.tizen.gallery3d.js
1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
2 //>>description: 3D photo gallery widget.\r
3 //>>label: Gallery3d\r
4 //>>group: Tizen:Widgets\r
5 \r
6 define( [ "components/imageloader", "components/motionpath", "components/webgl" ], function ( ) {\r
7 //>>excludeEnd("jqmBuildExclude");\r
8 \r
9 \r
10 /* ***************************************************************************\r
11  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.\r
12  *\r
13  * Permission is hereby granted, free of charge, to any person obtaining a\r
14  * copy of this software and associated documentation files (the "Software"),\r
15  * to deal in the Software without restriction, including without limitation\r
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
17  * and/or sell copies of the Software, and to permit persons to whom the\r
18  * Software is furnished to do so, subject to the following conditions:\r
19  *\r
20  * The above copyright notice and this permission notice shall be included in\r
21  * all copies or substantial portions of the Software.\r
22  *\r
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
29  * DEALINGS IN THE SOFTWARE.\r
30  * ***************************************************************************\r
31  *\r
32  * Authors: Hyunsook Park <hyunsook.park@samsung.com>\r
33  *                      Wonseop Kim <wonseop.kim@samsung.com>\r
34  */\r
35 \r
36 /**\r
37  *      'Gallery3D' is a 3D photo gallery widget.\r
38  *      Images are arranged with a S-shaped curve on a 3-dimensional coordinate system.\r
39  *      A user can rotate images by swiping the widget area.\r
40  *      To improve performance, the size of image(s) displayed on the screen should be a square(under\r
41  *      128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image\r
42  *      resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"\r
43  *      option resizes the gallery images under 128x128 pixels and stores the images on a local storage.\r
44  *      So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve\r
45  *      launching time. A browser or web runtime engine should support "Web Storage" feature to use that\r
46  *      option.)\r
47  *\r
48  *      HTML Attributes:\r
49  *\r
50  *              data-thumbnail-cache : Determines whether to cache and resize images.\r
51  *\r
52  *      APIs:\r
53  *\r
54  *              next ( void )\r
55  *                      : This method moves each image forward one by one.\r
56  *              prev ( void )\r
57  *                      : This method moves each image backward one by one.\r
58  *              select ( [number] )\r
59  *                      : When the "select" method is called with an argument, the method selects the image of given index.\r
60  *                      If the method is called with no argument, it will return the Javascript object having "src"\r
61  *                      attribute having the selected image’s URL.\r
62  *              add ( object or string [, number] )\r
63  *                      This method adds an image to Gallery3D widget.\r
64  *                      If the second argument isn't inputted, the image is added at the 0th position.\r
65  *              remove ( [number] )\r
66  *                      : This method deletes an image from Gallery3d widget.\r
67  *                      The argument defines the index of the image to be deleted.\r
68  *                      If an argument isn't inputted, it removes current image.\r
69  *              clearThumbnailCache ( void )\r
70  *                      : This method clears the cache data of all images when thumbnailCache option is set as 'true'.\r
71  *              refresh ( void )\r
72  *                      : This method updates and redraws current widget.\r
73  *              empty ( void )\r
74  *                      : This method removes all of images from Gallery3D widget.\r
75  *              length ( void )\r
76  *                      : This method gets the number of images.\r
77  *\r
78  *      Events:\r
79  *\r
80  *              select : Triggered when an image is selected.\r
81  *\r
82  *      Examples:\r
83  *\r
84  *              <script>\r
85  *                      $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
86  *                              $( "#gallery3d" ).gallery3d( "add", "01.jpg" );\r
87  *                      });\r
88  *              </script>\r
89  *              <div id="gallery3d" data-role="gallery3d"></div>\r
90  */\r
91 \r
92 /**\r
93         @class Gallery3D\r
94         The gallery3d widget is a 3D photo gallery widget.\r
95         Images are arranged with a S-shaped curve on a 3-dimensional coordinate system.\r
96         A user can rotate images by swiping the widget area.\r
97         <br/><br/>To add an gallery3d widget to the application, use the following code:\r
98 \r
99                 <script>\r
100                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
101                                 $( "#gallery3d" ).gallery3d( "add", "01.jpg" );\r
102                         });\r
103                 </script>\r
104                 <div id="gallery3d" data-role="gallery3d"></div>\r
105 */\r
106 /**\r
107         @property {Boolean} data-thumbnail-cache\r
108         Determines whether to cache and resize images.\r
109         To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).\r
110         "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.\r
111         So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.\r
112         A browser or web runtime engine must support "Web Storage" feature to use this option.\r
113 */\r
114 /**\r
115         @event select\r
116         Triggered when an image is selected.\r
117 \r
118                 <script>\r
119                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
120                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
121                                         .gallery3d( "add", { src: "2.jpg" } )\r
122                                         .gallery3d( "add", { src: "3.jpg" } );\r
123                         }).on( "select", function ( event, data, index ) {\r
124                                 // Handle the select event\r
125                                 var urlOfImage = data.src, indexOfImage = index;\r
126                         });\r
127                 </script>\r
128                 <div id="gallery3d" data-role="gallery3d"></div>\r
129 */\r
130 /**\r
131         @method next\r
132         This method moves each image forward one by one.\r
133 \r
134                 <script>\r
135                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
136                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
137                                         .gallery3d( "add", { src: "2.jpg" } )\r
138                                         .gallery3d( "add", { src: "3.jpg" } )\r
139                                         .gallery3d( "next" );\r
140                         });\r
141                 </script>\r
142                 <div id="gallery3d" data-role="gallery3d"></div>\r
143 */\r
144 /**\r
145         @method prev\r
146         This method moves each image backward one by one.\r
147 \r
148                 <script>\r
149                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
150                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
151                                         .gallery3d( "add", { src: "2.jpg" } )\r
152                                         .gallery3d( "add", { src: "3.jpg" } )\r
153                                         .gallery3d( "prev" );\r
154                         });\r
155                 </script>\r
156                 <div id="gallery3d" data-role="gallery3d"></div>\r
157 */\r
158 /**\r
159         @method select\r
160         When the "select" method is called with an argument, the method selects the image of given index.\r
161         If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image’s URL.\r
162 \r
163                 <script>\r
164                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
165                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
166                                         .gallery3d( "add", { src: "2.jpg" } )\r
167                                         .gallery3d( "add", { src: "3.jpg" } );\r
168                                 var selectedImage = $("#gallery3d"). gallery3d( "select" );\r
169                                 // selectedImage = { src: "3.jpg" };\r
170                         });\r
171                 </script>\r
172                 <div id="gallery3d" data-role="gallery3d"></div>\r
173 */\r
174 /**\r
175         @method add\r
176         This method adds an image to Gallery3D widget.\r
177         The first argument is a Javascript object having a "src" attribute or a string of image's path.\r
178         The second argument is an index of images.\r
179         If second argument isn't inputted, the image is added at the 0th position.\r
180 \r
181                 <script>\r
182                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
183                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
184                                         .gallery3d( "add", "2.jpg", 1 );\r
185                         });\r
186                 </script>\r
187                 <div id="gallery3d" data-role="gallery3d"></div>\r
188 */\r
189 /**\r
190         @method remove\r
191         This method deletes an image from Gallery3d widget.\r
192         The argument defines the index of the image to be deleted.\r
193         If an argument isn't inputted, it removes current image.\r
194 \r
195                 <script>\r
196                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
197                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
198                                         .gallery3d( "add", { src: "2.jpg" } )\r
199                                         .gallery3d( "add", { src: "3.jpg" } );\r
200 \r
201                                 $( "#gallery3d" ).gallery3d( "remove" );\r
202                                 $( "#gallery3d" ).gallery3d( "remove", 1 );\r
203                         });\r
204                 </script>\r
205                 <div id="gallery3d" data-role="gallery3d"></div>\r
206 */\r
207 /**\r
208         @method clearThumbnailCache\r
209         This method clears the cache data of all images when thumbnailCache option is set as 'true'\r
210 \r
211                 <script>\r
212                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
213                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
214                                         .gallery3d( "add", { src: "2.jpg" } )\r
215                                         .gallery3d( "add", { src: "3.jpg" } );\r
216 \r
217                                 $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );\r
218                         });\r
219                 </script>\r
220                 <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>\r
221 */\r
222 /**\r
223         @method refresh\r
224         This method updates and redraws current widget.\r
225 \r
226                 <script>\r
227                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
228                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
229                                         .gallery3d( "add", { src: "2.jpg" } )\r
230                                         .gallery3d( "add", { src: "3.jpg" } );\r
231 \r
232                                 $( "#gallery3d" ).gallery3d( "refresh" );\r
233                         });\r
234                 </script>\r
235                 <div id="gallery3d" data-role="gallery3d"></div>\r
236 */\r
237 /**\r
238         @method empty\r
239         This method removes all of images from Gallery3D widget.\r
240 \r
241                 <script>\r
242                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
243                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
244                                         .gallery3d( "add", { src: "2.jpg" } )\r
245                                         .gallery3d( "add", { src: "3.jpg" } );\r
246 \r
247                                 $( "#gallery3d" ).gallery3d( "empty" );\r
248                         });\r
249                 </script>\r
250                 <div id="gallery3d" data-role="gallery3d"></div>\r
251 */\r
252 /**\r
253         @method length\r
254         This method gets the number of images.\r
255 \r
256                 <script>\r
257                         $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
258                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
259                                         .gallery3d( "add", { src: "2.jpg" } )\r
260                                         .gallery3d( "add", { src: "3.jpg" } );\r
261 \r
262                                 var imagesLength = $( "#gallery3d" ).gallery3d( "length" );\r
263                                 // imagesLength = 3;\r
264                         });\r
265                 </script>\r
266                 <div id="gallery3d" data-role="gallery3d"></div>\r
267 */\r
268 \r
269 ( function ( $, document, window, undefined ) {\r
270         window.requestAnimationFrame = ( function () {\r
271                 return function ( callback ) {\r
272                         var id = window.setTimeout( callback, 1000 / 60 );\r
273                         return id;\r
274                 };\r
275         } () );\r
276 \r
277         window.cancelAnimationFrame = ( function () {\r
278                 return function ( id ) {\r
279                         window.clearTimeout( id );\r
280                 };\r
281         } () );\r
282 \r
283         var vec3 = window.vec3,\r
284                 mat3 = window.mat3,\r
285                 mat4 = window.mat4,\r
286                 GlArray32 = ( typeof window.Float32Array !== "undefined" ? window.Float32Array : ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) ),\r
287                 GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array ),\r
288                 getContext3D = function ( canvas ) {\r
289                         var gl, i,\r
290                                 contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];\r
291 \r
292                         for ( i = 0; i < contextNames.length; i += 1 ) {\r
293                                 try {\r
294                                         gl = canvas.getContext( contextNames[i] );\r
295                                         if ( gl ) {\r
296                                                 break;\r
297                                         }\r
298                                 } catch ( e ) {\r
299                                         window.alert( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );\r
300                                         return;\r
301                                 }\r
302                         }\r
303                         return gl;\r
304                 },\r
305                 VERTEX_SHADER = [\r
306                         "attribute vec3 aVertexPosition;",\r
307                         "attribute vec2 aTextureCoord;",\r
308                         "attribute vec3 aVertexNormal;",\r
309                         "uniform mat4 uMoveMatrix;",\r
310                         "uniform mat4 uPerspectiveMatrix;",\r
311                         "uniform mat3 nNormalMatrix;",\r
312                         "uniform vec3 uAmbientColor;",\r
313                         "uniform vec3 uLightDirection;",\r
314                         "uniform vec3 uDirectionColor;",\r
315                         "uniform vec3 uLightDirection_first;",\r
316                         "uniform vec3 uLightDirection_second;",\r
317                         "varying vec2 vTextureCoord;",\r
318                         "varying vec3 vLightWeight;",\r
319                         "varying vec4 vFogWeight;",\r
320 \r
321                         "void main(void) {",\r
322                         "       vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",\r
323                         "       gl_Position = uPerspectiveMatrix * v_Position;",\r
324                         "       vTextureCoord = aTextureCoord;",\r
325                         "       float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",\r
326                         "       vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.0, 1.0);",\r
327                         "       vec3 transNormalVector = nNormalMatrix * aVertexNormal;",\r
328 \r
329                         "       float vLightWeightFirst = 0.0;",\r
330                         "       float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",\r
331 \r
332                         "       vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",\r
333                         "}"\r
334                 ].join( "\n" ),\r
335                 FRAGMENT_SHADER = [\r
336                         "precision mediump float;",\r
337                         "varying vec2 vTextureCoord;",\r
338                         "varying vec3 vLightWeight;",\r
339                         "uniform sampler2D uSampler;",\r
340                         "varying vec4 vFogWeight;",\r
341 \r
342                         "void main(void) {",\r
343                         "       vec4 TextureColor = (texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t))) * vFogWeight;",\r
344                         "       gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",\r
345                         "}"\r
346                 ].join( "\n" );\r
347 \r
348         function Node() {\r
349                 this.vertices = [\r
350                         -1.0, -1.0, 0.0,\r
351                         1.0, -1.0, 0.0,\r
352                         1.0,  1.0, 0.0,\r
353                         -1.0,  1.0, 0.0\r
354                 ];\r
355                 this.textureCoords = [\r
356                         1.0, 0.0,\r
357                         0.0, 0.0,\r
358                         0.0, 1.0,\r
359                         1.0, 1.0\r
360                 ];\r
361                 this.normalVectors = [\r
362                         0.0, 0.0, 1.0,\r
363                         0.0, 0.0, 1.0,\r
364                         0.0, 0.0, 1.0,\r
365                         0.0, 0.0, 1.0\r
366                 ];\r
367                 this.texture = null;\r
368                 this.textureBuffer = null;\r
369                 this.textureBufferItemSize = 0;\r
370                 this.mashOrder = [];\r
371                 this.mvMatrix = null;\r
372                 this.level = -1;\r
373                 this.targetLevel = 0;\r
374                 this.drawable = false;\r
375                 this.image = null;\r
376                 this.imageID = 0;\r
377         }\r
378 \r
379         $.widget( "tizen.gallery3d", $.mobile.widget, {\r
380                 options: {\r
381                         thumbnailCache: false\r
382                 },\r
383 \r
384                 _MAX_ITEM_COUNT: 28,\r
385                 _ANIMATION_END: 999,\r
386                 _DURATION_DEFAULT: 300,\r
387                 _DURATION_FIRST: 1600,\r
388                 _VIEWPORT_WIDTH: 1024,\r
389                 _VIEWPORT_HEIGHT: 456,\r
390                 _DIRECTION_LEFT: -1,\r
391                 _DIRECTION_RIGHT: +1,\r
392 \r
393                 _gl: null,\r
394                 _shaderProgram : null,\r
395                 _positionBuffer : null,\r
396                 _textureCoordBuffer : null,\r
397                 _normalVectorBuffer : null,\r
398                 _nodes: null,\r
399                 _pMatrix : null,\r
400                 _animationID: 0,\r
401                 _dragInterval : 0,\r
402                 _startTime : 0,\r
403                 _sumTime : 0,\r
404                 _lightsPositionStack : [\r
405                         [0.0, 0.0, -1.0],       // back\r
406                         [-0.2, 0.0, 0.7]        // front\r
407                 ],\r
408                 _path: null,\r
409                 _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),\r
410                 _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),\r
411                 _canvas: null,\r
412                 _imageList: [],\r
413                 _maxDrawLength: 0,\r
414                 _firstImageNumber: 0,\r
415                 _lastImageNumber: 0,\r
416 \r
417                 _create: function () {\r
418                         var self = this,\r
419                                 view = self.element,\r
420                                 option = self.options;\r
421 \r
422                         self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );\r
423 \r
424                         view.addClass( "ui-gallery3d" ).append( self._canvas );\r
425                         self._addBehavier();\r
426 \r
427                         self._dragInterval = 1000 / 30; // 30fps\r
428 \r
429                         $.each( self.options, function ( key, value ) {\r
430                                 self.options[ key ] = undefined;\r
431                                 self._setOption( key, value );\r
432                         });\r
433 \r
434                 },\r
435 \r
436                 _setOption: function ( key, value ) {\r
437                         switch ( key ) {\r
438                         case "thumbnailCache" :\r
439                                 if ( typeof value === "string" ) {\r
440                                         value = ( value === "true" ) ? true : false;\r
441                                 } else {\r
442                                         value = !!value;\r
443                                 }\r
444                                 this._reset();\r
445                                 break;\r
446                         }\r
447 \r
448                         $.mobile.widget.prototype._setOption.call( this, key, value );\r
449                 },\r
450 \r
451                 _init: function ( canvas ) {\r
452                         var self = this,\r
453                                 pathPoints = [\r
454                                         [40, 0, -48],\r
455                                         [-12, 0, -40],  // contorl Point of Point1\r
456                                         [24, 0, -9],            // contorl Point of Point2\r
457                                         [-5, 0, -5]\r
458                                 ],\r
459                                 i;\r
460 \r
461                         canvas = canvas || self._canvas;\r
462 \r
463                         if ( !canvas ) {\r
464                                 return;\r
465                         }\r
466 \r
467                         self._gl = self._gl || self._initGL( canvas[0] );\r
468                         if ( !self._gl ) {\r
469                                 return;\r
470                         }\r
471 \r
472                         if ( !self._imageList ) {\r
473                                 return;\r
474                         }\r
475 \r
476                         self._shaderProgram = self._shaderProgram || self._initShader( self._gl );\r
477                         if ( !self._shaderProgram ) {\r
478                                 return;\r
479                         }\r
480 \r
481                         if ( self._imageList.length > self._MAX_ITEM_COUNT ) {\r
482                                 self._firstImageNumber = self._imageList.length - 1;\r
483                                 self._lastImageNumber = self._MAX_ITEM_COUNT - 1;\r
484                         }\r
485 \r
486                         self._nodes = self._initBuffers( self._gl, self._shaderProgram );\r
487                         self._initTextures( self._gl, self._nodes );\r
488                         self._path = $.motionpath( "bspline", {\r
489                                 points: pathPoints,\r
490                                 maxLevel: self._MAX_ITEM_COUNT\r
491                         } );\r
492                         for ( i = 0; i < self._nodes.length; i += 1 ) {\r
493                                 self._path.levels[i] = self._path.levels[i + 1] || 0;\r
494                                 self._nodes[i].level = i;\r
495                         }\r
496                 },\r
497 \r
498                 _final: function ( canvas ) {\r
499                         var self = this,\r
500                                 gl = self._gl;\r
501 \r
502                         if ( !gl ) {\r
503                                 return;\r
504                         }\r
505 \r
506                         canvas = canvas || self._canvas;\r
507 \r
508                         $( self._nodes ).each( function ( i ) {\r
509                                 var node = self._nodes[i];\r
510                                 gl.deleteTexture( node.texture );\r
511                                 node.texture = null;\r
512                         });\r
513                         self._nodes = null;\r
514 \r
515                         gl.deleteBuffer( self._positionBuffer );\r
516                         self._positionBuffer = null;\r
517                         gl.deleteBuffer( self._textureCoordBuffer );\r
518                         self._textureCoordBuffer = null;\r
519                         gl.deleteBuffer( self._normalVectorBuffer );\r
520                         self._normalVectorBuffer = null;\r
521 \r
522                         $.webgl.shader.deleteShaders( gl );\r
523                         gl.deleteProgram( self._shaderProgram );\r
524                         self._shaderProgram = null;\r
525 \r
526                         self._gl = gl = null;\r
527                 },\r
528 \r
529                 _addBehavier : function () {\r
530                         var self = this,\r
531                                 view = self.element,\r
532                                 canvas = self._canvas,\r
533                                 touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),\r
534                                 touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",\r
535                                 touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",\r
536                                 touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";\r
537 \r
538                         $( document ).unbind( ".gallery3d" ).bind( "pagechange.gallery3d", function ( e ) {\r
539                                 $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );\r
540                         }).bind( "pageremove.gallery3d", function ( e ) {\r
541                                 $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );\r
542                         });\r
543 \r
544                         $( window ).unbind( ".gallery3d" ).bind( "resize.gallery3d orientationchange.gallery3d", function ( e ) {\r
545                                 $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );\r
546                         }).bind( "unload.gallery3d", function ( e ) {\r
547                                 $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );\r
548                         });\r
549 \r
550                         view.bind( "_destory", function ( e ) {\r
551                                 self._final();\r
552                         });\r
553 \r
554                         canvas.bind( "webglcontextlost", function ( e ) {\r
555                                 e.preventDefault();\r
556                         }).bind( "webglcontextrestored", function ( e ) {\r
557                                 self._init();\r
558                         }).bind( touchStartEvt, function ( e ) {\r
559                                 var i = 0,\r
560                                         startX = 0,\r
561                                         deltaMaxSteps = 20,\r
562                                         deltas = [ deltaMaxSteps ],\r
563                                         deltaTimes = [ deltaMaxSteps ],\r
564                                         deltaIndex = 0,\r
565                                         dragValue = 0,\r
566                                         dragDirection = false,\r
567                                         prevTime = 0;\r
568 \r
569                                 e.preventDefault();\r
570                                 e.stopPropagation();\r
571 \r
572                                 if ( self._imageList.length <= 1 ) {\r
573                                         return;\r
574                                 }\r
575 \r
576                                 self._stop();\r
577 \r
578                                 startX =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
579                                 prevTime = $.now();\r
580 \r
581                                 for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
582                                         deltas[i] = startX;\r
583                                         deltaTimes[i] = $.now();\r
584                                 }\r
585 \r
586                                 deltaIndex += 1;\r
587 \r
588                                 view.bind( touchMoveEvt, function ( e ) {\r
589                                         var x, dx, interval;\r
590 \r
591                                         e.preventDefault();\r
592                                         e.stopPropagation();\r
593 \r
594                                         x =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
595                                         dx = startX - x;\r
596 \r
597                                         deltas[deltaIndex] = x;\r
598                                         deltaTimes[deltaIndex] = $.now();\r
599                                         interval = deltaTimes[deltaIndex] - prevTime;\r
600 \r
601                                         deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;\r
602 \r
603                                         // Validation of drag\r
604                                         if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {\r
605                                                 if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {\r
606                                                         dragValue = 0;\r
607                                                         dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;\r
608                                                 }\r
609 \r
610                                                 dragValue += Math.abs( dx ) / 100;\r
611                                                 if ( dragValue >= 1 ) {\r
612                                                         self._setPosition( self._ANIMATION_END, dragDirection );\r
613                                                         dragValue = 0;\r
614                                                 } else {\r
615                                                         self._setPosition( dragValue, dragDirection );\r
616                                                 }\r
617                                                 self._drawScene();\r
618                                                 startX = x;\r
619                                                 prevTime = $.now();\r
620                                         }\r
621                                 }).bind( touchEndEvt, function ( e ) {\r
622                                         var baseTime = 0,\r
623                                                 recent = -1,\r
624                                                 index = 0,\r
625                                                 previous = 0,\r
626                                                 baseTimeRatio = 0,\r
627                                                 fx = 0,\r
628                                                 lastX = 0,\r
629                                                 velocityX = 0,\r
630                                                 dx = 0,\r
631                                                 isSwipe = true,\r
632                                                 direction;\r
633 \r
634                                         e.preventDefault();\r
635                                         e.stopPropagation();\r
636 \r
637                                         // Validation of swipe\r
638                                         baseTime = $.now() - self._swipeThresholdOfBasetimeGap;\r
639                                         lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
640                                         dx = startX - lastX;\r
641                                         startX = 0;\r
642                                         for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
643                                                 index = ( deltaIndex + i ) % deltaMaxSteps;\r
644                                                 if ( deltaTimes[index] > baseTime ) {\r
645                                                         recent = index;\r
646                                                         break;\r
647                                                 }\r
648                                         }\r
649                                         if ( recent < 0 ) {\r
650                                                 isSwipe = false;\r
651                                         }\r
652 \r
653                                         if ( isSwipe ) {\r
654                                                 previous = recent;\r
655                                                 for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
656                                                         previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;\r
657                                                         if ( deltaTimes[previous] < deltaTimes[recent] ) {\r
658                                                                 break;\r
659                                                         }\r
660                                                 }\r
661                                                 // too slow or too fast\r
662                                                 if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {\r
663                                                         isSwipe = false;\r
664                                                 }\r
665                                         }\r
666 \r
667                                         if ( isSwipe ) {\r
668                                                 baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );\r
669                                                 fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];\r
670                                                 if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {\r
671                                                         fx = lastX;\r
672                                                 }\r
673                                                 velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );\r
674                                         }\r
675 \r
676                                         if ( isSwipe && velocityX ) {\r
677                                                 direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;\r
678                                                 self._run( direction, Math.abs( velocityX ), dragValue );\r
679                                         } else if ( dragDirection !== 0 && dragValue ) {\r
680                                                 self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );\r
681                                         }\r
682 \r
683                                         view.unbind( ".gallery3d" );\r
684                                 }).bind( touchLeaveEvt, function ( e ) {\r
685                                         view.trigger( touchEndEvt );\r
686                                 });\r
687                         });\r
688                 },\r
689 \r
690                 // ----------------------------------------------------------\r
691                 // Data parsing\r
692                 // ----------------------------------------------------------\r
693                 _loadData: function ( jsonUrl, key ) {\r
694                         var self = this;\r
695 \r
696                         $.ajax({\r
697                                 async : false,\r
698                                 url : jsonUrl,\r
699                                 dataType: "json",\r
700                                 success : function ( data ) {\r
701                                         self._imageList = $.extend( [], data[ key ] );\r
702                                 }\r
703                         });\r
704                 },\r
705 \r
706                 // ----------------------------------------------------------\r
707                 // WebGL\r
708                 // ----------------------------------------------------------\r
709                 _initGL: function ( canvas ) {\r
710                         var self = this,\r
711                                 gl;\r
712 \r
713                         gl = getContext3D( canvas );\r
714                         if ( !gl ) {\r
715                                 window.alert( "There's no WebGL context available!!!" );\r
716                                 return null;\r
717                         }\r
718 \r
719                         gl.enable( gl.BLEND );\r
720                         gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\r
721 \r
722                         gl.enable( gl.DEPTH_TEST );\r
723                         gl.depthFunc( gl.LEQUAL );\r
724 \r
725                         canvas.width = self._VIEWPORT_WIDTH;\r
726                         canvas.height = self._VIEWPORT_HEIGHT;\r
727                         gl.viewportWidth = canvas.width;\r
728                         gl.viewportHeight = canvas.height;\r
729                         gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );\r
730                         self._pMatrix = mat4.create();\r
731                         mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );\r
732 \r
733                         gl.clearColor( 0.0, 0.0, 0.0, 1.0 );\r
734                         gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );\r
735 \r
736                         return gl;\r
737                 },\r
738 \r
739                 _initShader : function ( gl ) {\r
740                         var self = this,\r
741                                 shaderProgram;\r
742 \r
743                         shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );\r
744                         gl.useProgram( shaderProgram );\r
745 \r
746                         shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );\r
747                         gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );\r
748 \r
749                         shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );\r
750                         gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );\r
751 \r
752                         // Set light normal vectors for lighting~\r
753                         shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );\r
754                         gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );\r
755 \r
756                         shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");\r
757                         shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");\r
758                         shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");\r
759 \r
760                         // Set light variables~\r
761                         shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");\r
762                         shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");\r
763                         shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");\r
764                         shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");\r
765                         shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");\r
766 \r
767                         return shaderProgram;\r
768                 },\r
769 \r
770                 _initBuffers: function ( gl, shaderProgram ) {\r
771                         var self = this,\r
772                                 i = 0,\r
773                                 mashBase = 0,\r
774                                 vertices = [],\r
775                                 textureCoords = [],\r
776                                 normalVectors = [],\r
777                                 nodes = [],\r
778                                 maxDrawLength = self._MAX_ITEM_COUNT;\r
779 \r
780                         for ( i = 0; i < self._imageList.length + 1; i += 1 ) {\r
781                                 nodes[i] = new Node();\r
782                                 $.merge( vertices, nodes[i].vertices );\r
783                                 $.merge( textureCoords, nodes[i].textureCoords );\r
784                                 $.merge( normalVectors, nodes[i].normalVectors );\r
785 \r
786                                 nodes[i].textureBuffer = gl.createBuffer();\r
787                                 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );\r
788                                 mashBase = i * 4;\r
789                                 nodes[i].meshOrder = [\r
790                                         mashBase, mashBase + 1, mashBase + 2,\r
791                                         mashBase + 2, mashBase + 3, mashBase\r
792                                 ];\r
793                                 gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );\r
794                                 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory\r
795                                 nodes[i].textureBufferItemSize = 6;\r
796                         }\r
797 \r
798                         self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );\r
799                         self._positionBuffer.itemSize = 3;\r
800 \r
801                         self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );\r
802                         self._textureCoordBuffer.itemSize = 2;\r
803 \r
804                         self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light\r
805                         self._normalVectorBuffer.itemSize = 3;\r
806 \r
807                         // Ambient light\r
808                         gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );\r
809                         // Direcntion light\r
810                         gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );\r
811 \r
812                         return nodes;\r
813                 },\r
814 \r
815                 // ----------------------------------------------------------\r
816                 // Texture\r
817                 // ----------------------------------------------------------\r
818                 _initTextures: function ( gl, nodes ) {\r
819                         var self = this;\r
820 \r
821                         $( nodes ).each( function ( i ) {\r
822                                 var node = nodes[i],\r
823                                         url;\r
824 \r
825                                 if ( !self._imageList[i] ) {\r
826                                         return false;\r
827                                 }\r
828 \r
829                                 url = self._imageList[i].src;\r
830                                 node.texture = gl.createTexture();\r
831                                 self._loadImage( url, i, i, gl, nodes );\r
832                         });\r
833                 },\r
834 \r
835                 _loadImage: function ( url, i, imageID, gl, nodes ) {\r
836                         var self = this,\r
837                                 isMipmap = false,\r
838                                 image,\r
839                                 node;\r
840 \r
841                         gl = gl || self._gl;\r
842                         nodes = nodes || self._nodes;\r
843                         isMipmap = isMipmap || false;\r
844                         node = nodes[i];\r
845                         node.image = node.image || new Image();\r
846 \r
847                         $( node.image ).one( "load", function ( e ) {\r
848                                 self._bindTexture( gl, node, this, isMipmap );\r
849                                 node.imageID = imageID;\r
850 \r
851                                 if ( !self._animationID ) {\r
852                                         self._setPosition( 0, 0 );\r
853                                 }\r
854                         });\r
855 \r
856                         if ( self.options.thumbnailCache ) {\r
857                                 $.imageloader.getThumbnail( url, function ( result ) {\r
858                                         if ( result === "NOT_FOUND_ERR" ) {\r
859                                                 $.imageloader.setThumbnail( url, function ( result ) {\r
860                                                         if ( result && result.length > 30 ) {\r
861                                                                 node.image.src = result;\r
862                                                                 isMipmap = true;\r
863                                                         } else {\r
864                                                                 node.image.src = url;\r
865                                                         }\r
866                                                 });\r
867                                         } else if ( result && result.length > 30 ) {\r
868                                                 node.image.src = result;\r
869                                                 isMipmap = true;\r
870                                         } else {\r
871                                                 node.image.src = url;\r
872                                         }\r
873                                 });\r
874                         } else {\r
875                                 node.image.src = url;\r
876                         }\r
877                 },\r
878 \r
879                 _bindTexture: function ( gl, node, image, isMipmap ) {\r
880                         if ( !node || !node.texture ) {\r
881                                 return;\r
882                         }\r
883 \r
884                         gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );\r
885 \r
886                         gl.bindTexture( gl.TEXTURE_2D, node.texture );\r
887                         gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );\r
888 \r
889                         if ( isMipmap ) {\r
890                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\r
891                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );\r
892                                 gl.generateMipmap( gl.TEXTURE_2D );\r
893                         } else {\r
894                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\r
895                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );\r
896                         }\r
897 \r
898                         gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\r
899                         gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\r
900 \r
901                         node.texture.loaded = true;\r
902 \r
903                         // release texture memory\r
904                         gl.bindTexture( gl.TEXTURE_2D, null );\r
905                 },\r
906 \r
907                 // ----------------------------------------------------------\r
908                 // rendering\r
909                 // ----------------------------------------------------------\r
910                 _setPosition: function ( progress, direction ) {\r
911                         var self = this,\r
912                                 nodes = self._nodes,\r
913                                 imageList = self._imageList,\r
914                                 imageListLength = imageList.length,\r
915                                 itemCount = self._MAX_ITEM_COUNT,\r
916                                 displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,\r
917                                 nextLevelLenth = 0,\r
918                                 i = 0,\r
919                                 t = 0,\r
920                                 position = 0,\r
921                                 angle = 0,\r
922                                 current = 0,\r
923                                 next = 0,\r
924                                 nextLevel = 0,\r
925                                 path = self._path,\r
926                                 nextImageID = 0;\r
927 \r
928                         nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;\r
929 \r
930                         if ( !nodes[i].level ) {\r
931                                 nodes[i].level = displayLength;\r
932                         }\r
933 \r
934                         for ( i = 0; i < displayLength; i += 1 ) {\r
935                                 if ( !nodes[i].mvMatrix ) {\r
936                                         nodes[i].mvMatrix = mat4.create();\r
937                                 }\r
938 \r
939                                 if ( direction > 0 && nodes[i].level >= displayLength ) {\r
940                                         nodes[i].level = 0;\r
941                                 }\r
942 \r
943                                 current = path.levels[nodes[i].level];\r
944                                 nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;\r
945                                 next = path.levels[nextLevel];\r
946 \r
947                                 if ( imageListLength > itemCount ) {\r
948                                         if ( direction > 0 && nextLevel === 1\r
949                                                         && self._firstImageNumber !== nodes[i].imageID ) {\r
950                                                 self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );\r
951                                         } else if ( direction < 0 && nextLevel === nextLevelLenth - 1\r
952                                                         && self._lastImageNumber !== nodes[i].imageID ) {\r
953                                                 self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );\r
954                                         }\r
955                                 }\r
956 \r
957                                 mat4.identity( nodes[i].mvMatrix );\r
958                                 mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );\r
959                                 mat4.rotate( nodes[i].mvMatrix, self._degreeToRadian( 19 ), [1, 0, 0] );\r
960 \r
961                                 t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );\r
962 \r
963                                 if ( progress >= self._ANIMATION_END ) {\r
964                                         nodes[i].level = nextLevel || displayLength;\r
965                                         t = path.levels[nodes[i].level];\r
966                                 }\r
967 \r
968                                 if ( ( progress < self._ANIMATION_END )\r
969                                                 && ( direction <= 0 && nodes[i].level < 1 ) ) {\r
970                                         nodes[i].drawable = false;\r
971                                 } else {\r
972                                         nodes[i].drawable = true;\r
973                                 }\r
974 \r
975                                 if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {\r
976                                         self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );\r
977                                 }\r
978 \r
979                                 position = path.getPosition( t );\r
980                                 angle = path.getAngle( t );\r
981 \r
982                                 mat4.translate( nodes[i].mvMatrix, position );\r
983                                 mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );\r
984                         }\r
985 \r
986                         if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {\r
987                                 self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;\r
988                                 if ( self._firstImageNumber < 0 ) {\r
989                                         self._firstImageNumber = imageListLength - 1;\r
990                                 }\r
991 \r
992                                 self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;\r
993                                 if ( self._lastImageNumber < 0 ) {\r
994                                         self._lastImageNumber = imageListLength - 1;\r
995                                 }\r
996                         }\r
997                         self._drawScene();\r
998                 },\r
999 \r
1000                 _drawScene: function () {\r
1001                         if ( !this._gl || !this._shaderProgram ) {\r
1002                                 return;\r
1003                         }\r
1004 \r
1005                         var self = this,\r
1006                                 gl = self._gl,\r
1007                                 shaderProgram = self._shaderProgram,\r
1008                                 nodes = self._nodes,\r
1009                                 nodesLength = nodes.length,\r
1010                                 i;\r
1011 \r
1012                         gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );\r
1013 \r
1014                         gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );\r
1015                         gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
1016 \r
1017                         gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );\r
1018                         gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
1019 \r
1020                         gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );\r
1021                         gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
1022 \r
1023                         for ( i = 0; i < nodesLength; i += 1 ) {\r
1024                                 if ( nodes[i].drawable ) {\r
1025                                         self._drawElement( self._pMatrix, nodes[i] );\r
1026                                 }\r
1027                         }\r
1028                 },\r
1029 \r
1030                 _drawElement: function ( perspectiveMatrix, targetNode ) {\r
1031                         var self = this,\r
1032                                 gl = self._gl,\r
1033                                 shaderProgram = self._shaderProgram,\r
1034                                 moveMatrix = targetNode.mvMatrix,\r
1035                                 texture = targetNode.texture,\r
1036                                 meshIndexBuffer = targetNode.textureBuffer,\r
1037                                 meshIndexBufferItemSize = targetNode.textureBufferItemSize,\r
1038                                 lightPositions = self._lightsPositionStack,\r
1039                                 LightDir,\r
1040                                 normalMatrix;\r
1041 \r
1042                         if ( !moveMatrix ) {\r
1043                                 return;\r
1044                         }\r
1045 \r
1046                         gl.activeTexture( gl.TEXTURE0 );\r
1047                         if ( texture && texture.loaded ) {\r
1048                                 gl.bindTexture( gl.TEXTURE_2D, texture );\r
1049                         }\r
1050                         gl.uniform1i( shaderProgram.sampleUniform, 0 );\r
1051 \r
1052                         LightDir = vec3.create();\r
1053                         vec3.normalize( lightPositions[0], LightDir );\r
1054                         vec3.scale( LightDir, -8 );\r
1055                         gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );\r
1056 \r
1057                         vec3.normalize( lightPositions[1], LightDir );\r
1058                         vec3.scale( LightDir, -1 );\r
1059                         gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );\r
1060                         gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );\r
1061 \r
1062                         gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );\r
1063                         gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );\r
1064 \r
1065                         normalMatrix = mat3.create();\r
1066                         mat4.toInverseMat3( moveMatrix, normalMatrix );\r
1067                         mat3.transpose( normalMatrix );\r
1068                         gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );\r
1069 \r
1070                         gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );\r
1071 \r
1072                         // release buffer memory\r
1073                         gl.bindBuffer( gl.ARRAY_BUFFER, null );\r
1074                         gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );\r
1075 \r
1076                         // release texture memory\r
1077                         gl.bindTexture( gl.TEXTURE_2D, null );\r
1078                 },\r
1079 \r
1080                 // ----------------------------------------------------------\r
1081                 // Animation\r
1082                 // ----------------------------------------------------------\r
1083                 _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {\r
1084                         var self = this,\r
1085                                 timeNow = $.now(),\r
1086                                 progress,\r
1087                                 removeCount = 0;\r
1088 \r
1089                         easingType = easingType || "linear";\r
1090                         startValue = startValue || 0;\r
1091                         _removeCount = _removeCount || 0;\r
1092 \r
1093                         if ( self._sumTime >= duration ) {\r
1094                                 self._setPosition( self._ANIMATION_END, direction );\r
1095                                 self._stop();\r
1096                                 return;\r
1097                         }\r
1098 \r
1099                         if ( self._startTime === 0 ) {\r
1100                                 self._startTime = timeNow;\r
1101                         } else {\r
1102                                 self._sumTime = timeNow - self._startTime;\r
1103                                 progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );\r
1104                                 removeCount = parseInt( Math.abs( progress ), 10 );\r
1105 \r
1106                                 if ( _removeCount !== removeCount ) {\r
1107                                         self._setPosition( self._ANIMATION_END, direction );\r
1108                                         _removeCount = removeCount;\r
1109 \r
1110                                         if ( ( repeatCount - _removeCount ) >= 0 ) {\r
1111                                                 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );\r
1112                                         } else {\r
1113                                                 self._stop();\r
1114                                         }\r
1115                                         return;\r
1116                                 }\r
1117 \r
1118                                 self._setPosition( progress - _removeCount, direction );\r
1119                         }\r
1120 \r
1121                         self._animationID = window.requestAnimationFrame( function () {\r
1122                                 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );\r
1123                         });\r
1124                 },\r
1125 \r
1126                 _run: function ( direction, repeatCount, startValue ) {\r
1127                         var self = this,\r
1128                                 repeat = repeatCount || 0,\r
1129                                 duration = self._DURATION_DEFAULT * ( repeat + 1 );\r
1130 \r
1131                         if ( self._imageList.length <= 1 ) {\r
1132                                 return;\r
1133                         }\r
1134 \r
1135                         startValue = startValue || 0;\r
1136                         duration = ( duration >= 0 ) ? duration : 0;\r
1137 \r
1138                         if ( self._animationID ) {\r
1139                                 self._setPosition( self._ANIMATION_END, direction );\r
1140                                 self._stop();\r
1141                         }\r
1142 \r
1143                         self._animate( "easeOutExpo", duration, direction, repeat, startValue );\r
1144                 },\r
1145 \r
1146                 _reset: function () {\r
1147                         if ( !this._canvas || !this._gl ) {\r
1148                                 return;\r
1149                         }\r
1150 \r
1151                         this._final();\r
1152                         this._init();\r
1153                         this.refresh();\r
1154                 },\r
1155 \r
1156                 _stop: function () {\r
1157                         if ( this._animationID ) {\r
1158                                 window.cancelAnimationFrame( this._animationID );\r
1159                         }\r
1160                         this._animationID = 0;\r
1161 \r
1162                         this._startTime = 0;\r
1163                         this._sumTime = 0;\r
1164                 },\r
1165 \r
1166                 _degreeToRadian: function ( degree ) {\r
1167                         return degree * Math.PI / 180;\r
1168                 },\r
1169 \r
1170                 next: function () {\r
1171                         this._run( this._DIRECTION_LEFT , 0 );\r
1172                 },\r
1173 \r
1174                 prev: function () {\r
1175                         this._run( this._DIRECTION_RIGHT, 0 );\r
1176                 },\r
1177 \r
1178                 refresh: function () {\r
1179                         var view = this.element,\r
1180                                 canvas = view.find( "canvas.ui-gallery3d-canvas" );\r
1181 \r
1182                         if ( canvas.width() !== view.width() ) {\r
1183                                 canvas.width( view.width() );\r
1184                         }\r
1185 \r
1186                         if ( !this._animationID ) {\r
1187                                 this._setPosition( 0, 0 );\r
1188                         }\r
1189                 },\r
1190 \r
1191                 select: function ( index ) {\r
1192                         var nodes = this._nodes,\r
1193                                 repeat,\r
1194                                 i,\r
1195                                 imageID,\r
1196                                 object = null,\r
1197                                 target = 0,\r
1198                                 direction = 0;\r
1199 \r
1200                         if ( index && this._animationID ) {\r
1201                                 this._stop();\r
1202                         }\r
1203 \r
1204                         for ( i in nodes ) {\r
1205                                 if ( nodes[i].level === 1 ) {\r
1206                                         object = this._imageList[ nodes[i].imageID ];\r
1207                                         imageID = nodes[i].imageID;\r
1208                                         break;\r
1209                                 }\r
1210                         }\r
1211 \r
1212                         if ( !index ) {\r
1213                                 return object;\r
1214                         }\r
1215 \r
1216                         if ( index < 0 && index >= this._imageList.length ) {\r
1217                                 return;\r
1218                         }\r
1219 \r
1220                         target = index - imageID;\r
1221                         direction = ( target > 0 ) ? this._DIRECTION_LEFT\r
1222                                 : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );\r
1223                         if ( direction ) {\r
1224                                 this._run( direction, Math.abs( target ) - 1  );\r
1225                         }\r
1226                 },\r
1227 \r
1228                 add: function ( item, index ) {\r
1229                         if ( !item ) {\r
1230                                 return;\r
1231                         }\r
1232 \r
1233                         if ( typeof item === "string" ) {\r
1234                                 item = { "src" : item };\r
1235                         }\r
1236 \r
1237                         index = index || 0;\r
1238                         if ( typeof index !== "number" && index < 0\r
1239                                         && index >= this._imageList.length ) {\r
1240                                 return;\r
1241                         }\r
1242 \r
1243                         this._imageList.splice( index, 0, item );\r
1244                         if ( this._gl ) {\r
1245                                 this._reset();\r
1246                         }\r
1247                 },\r
1248 \r
1249                 remove: function ( index ) {\r
1250                         index = index || 0;\r
1251                         if ( typeof index !== "number" && index < 0\r
1252                                         && index >= this._imageList.length ) {\r
1253                                 return;\r
1254                         }\r
1255 \r
1256                         this._imageList.splice( index, 1 );\r
1257                         if ( this._gl ) {\r
1258                                 this._reset();\r
1259                         }\r
1260                 },\r
1261 \r
1262                 clearThumbnailCache: function () {\r
1263                         if ( !this._nodes || ( this._nodes.length <= 0 ) ) {\r
1264                                 return;\r
1265                         }\r
1266 \r
1267                         var i, url;\r
1268                         for ( i = 0; i < this._imageList.length; i += 1 ) {\r
1269                                 url = this._imageList[i].src;\r
1270                                 $.imageloader.removeThumbnail( url );\r
1271                         }\r
1272                 },\r
1273 \r
1274                 empty: function () {\r
1275                         this._imageList = [];\r
1276                         this._reset();\r
1277                 },\r
1278 \r
1279                 length: function () {\r
1280                         return this._imageList.length;\r
1281                 }\r
1282         });\r
1283 \r
1284         $( document ).bind( "pagecreate create", function ( e ) {\r
1285                 $( ":jqmData(role='gallery3d')" ).gallery3d();\r
1286         });\r
1287 \r
1288 } ( jQuery, document, window ) );\r
1289 \r
1290 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
1291 } );\r
1292 //>>excludeEnd("jqmBuildExclude");