[TemporaryStorage] add files required for SDK build
[samples/web/TemporaryStorage.git] / tizen-web-ui-fw / latest / js / src / widgets / jquery.mobile.tizen.gallery3d.js
1
2
3 /* ***************************************************************************
4         Flora License
5
6         Version 1.1, April, 2013
7
8         http://floralicense.org/license/
9
10         TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
11
12         1. Definitions.
13
14         "License" shall mean the terms and conditions for use, reproduction,
15         and distribution as defined by Sections 1 through 9 of this document.
16
17         "Licensor" shall mean the copyright owner or entity authorized by
18         the copyright owner that is granting the License.
19
20         "Legal Entity" shall mean the union of the acting entity and
21         all other entities that control, are controlled by, or are
22         under common control with that entity. For the purposes of
23         this definition, "control" means (i) the power, direct or indirect,
24         to cause the direction or management of such entity,
25         whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
26         or more of the outstanding shares, or (iii) beneficial ownership of
27         such entity.
28
29         "You" (or "Your") shall mean an individual or Legal Entity
30         exercising permissions granted by this License.
31
32         "Source" form shall mean the preferred form for making modifications,
33         including but not limited to software source code, documentation source,
34         and configuration files.
35
36         "Object" form shall mean any form resulting from mechanical
37         transformation or translation of a Source form, including but
38         not limited to compiled object code, generated documentation,
39         and conversions to other media types.
40
41         "Work" shall mean the work of authorship, whether in Source or Object form,
42         made available under the License, as indicated by a copyright notice
43         that is included in or attached to the work (an example is provided
44         in the Appendix below).
45
46         "Derivative Works" shall mean any work, whether in Source or Object form,
47         that is based on (or derived from) the Work and for which the editorial
48         revisions, annotations, elaborations, or other modifications represent,
49         as a whole, an original work of authorship. For the purposes of this License,
50         Derivative Works shall not include works that remain separable from,
51         or merely link (or bind by name) to the interfaces of, the Work and
52         Derivative Works thereof.
53
54         "Contribution" shall mean any work of authorship, including the original
55         version of the Work and any modifications or additions to that Work or
56         Derivative Works thereof, that is intentionally submitted to Licensor
57         for inclusion in the Work by the copyright owner or by an individual or
58         Legal Entity authorized to submit on behalf of the copyright owner.
59         For the purposes of this definition, "submitted" means any form of
60         electronic, verbal, or written communication sent to the Licensor or
61         its representatives, including but not limited to communication on
62         electronic mailing lists, source code control systems, and issue
63         tracking systems that are managed by, or on behalf of, the Licensor
64         for the purpose of discussing and improving the Work, but excluding
65         communication that is conspicuously marked or otherwise designated
66         in writing by the copyright owner as "Not a Contribution."
67
68         "Contributor" shall mean Licensor and any individual or Legal Entity
69         on behalf of whom a Contribution has been received by Licensor and
70         subsequently incorporated within the Work.
71
72         "Tizen Certified Platform" shall mean a software platform that complies
73         with the standards set forth in the Tizen Compliance Specification
74         and passes the Tizen Compliance Tests as defined from time to time
75         by the Tizen Technical Steering Group and certified by the Tizen
76         Association or its designated agent.
77
78         2. Grant of Copyright License.  Subject to the terms and conditions of
79         this License, each Contributor hereby grants to You a perpetual,
80         worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81         copyright license to reproduce, prepare Derivative Works of,
82         publicly display, publicly perform, sublicense, and distribute the
83         Work and such Derivative Works in Source or Object form.
84
85         3. Grant of Patent License.  Subject to the terms and conditions of
86         this License, each Contributor hereby grants to You a perpetual,
87         worldwide, non-exclusive, no-charge, royalty-free, irrevocable
88         (except as stated in this section) patent license to make, have made,
89         use, offer to sell, sell, import, and otherwise transfer the Work
90         solely as incorporated into a Tizen Certified Platform, where such
91         license applies only to those patent claims licensable by such
92         Contributor that are necessarily infringed by their Contribution(s)
93         alone or by combination of their Contribution(s) with the Work solely
94         as incorporated into a Tizen Certified Platform to which such
95         Contribution(s) was submitted. If You institute patent litigation
96         against any entity (including a cross-claim or counterclaim
97         in a lawsuit) alleging that the Work or a Contribution incorporated
98         within the Work constitutes direct or contributory patent infringement,
99         then any patent licenses granted to You under this License for that
100         Work shall terminate as of the date such litigation is filed.
101
102         4. Redistribution.  You may reproduce and distribute copies of the
103         Work or Derivative Works thereof pursuant to the copyright license
104         above, in any medium, with or without modifications, and in Source or
105         Object form, provided that You meet the following conditions:
106
107           1. You must give any other recipients of the Work or Derivative Works
108                  a copy of this License; and
109           2. You must cause any modified files to carry prominent notices stating
110                  that You changed the files; and
111           3. You must retain, in the Source form of any Derivative Works that
112                  You distribute, all copyright, patent, trademark, and attribution
113                  notices from the Source form of the Work, excluding those notices
114                  that do not pertain to any part of the Derivative Works; and
115           4. If the Work includes a "NOTICE" text file as part of its distribution,
116                  then any Derivative Works that You distribute must include a readable
117                  copy of the attribution notices contained within such NOTICE file,
118                  excluding those notices that do not pertain to any part of
119                  the Derivative Works, in at least one of the following places:
120                  within a NOTICE text file distributed as part of the Derivative Works;
121                  within the Source form or documentation, if provided along with the
122                  Derivative Works; or, within a display generated by the Derivative Works,
123                  if and wherever such third-party notices normally appear.
124                  The contents of the NOTICE file are for informational purposes only
125                  and do not modify the License.
126
127         You may add Your own attribution notices within Derivative Works
128         that You distribute, alongside or as an addendum to the NOTICE text
129         from the Work, provided that such additional attribution notices
130         cannot be construed as modifying the License. You may add Your own
131         copyright statement to Your modifications and may provide additional or
132         different license terms and conditions for use, reproduction, or
133         distribution of Your modifications, or for any such Derivative Works
134         as a whole, provided Your use, reproduction, and distribution of
135         the Work otherwise complies with the conditions stated in this License.
136
137         5. Submission of Contributions. Unless You explicitly state otherwise,
138         any Contribution intentionally submitted for inclusion in the Work
139         by You to the Licensor shall be under the terms and conditions of
140         this License, without any additional terms or conditions.
141         Notwithstanding the above, nothing herein shall supersede or modify
142         the terms of any separate license agreement you may have executed
143         with Licensor regarding such Contributions.
144
145         6. Trademarks.  This License does not grant permission to use the trade
146         names, trademarks, service marks, or product names of the Licensor,
147         except as required for reasonable and customary use in describing the
148         origin of the Work and reproducing the content of the NOTICE file.
149
150         7. Disclaimer of Warranty. Unless required by applicable law or
151         agreed to in writing, Licensor provides the Work (and each
152         Contributor provides its Contributions) on an "AS IS" BASIS,
153         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
154         implied, including, without limitation, any warranties or conditions
155         of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
156         PARTICULAR PURPOSE. You are solely responsible for determining the
157         appropriateness of using or redistributing the Work and assume any
158         risks associated with Your exercise of permissions under this License.
159
160         8. Limitation of Liability. In no event and under no legal theory,
161         whether in tort (including negligence), contract, or otherwise,
162         unless required by applicable law (such as deliberate and grossly
163         negligent acts) or agreed to in writing, shall any Contributor be
164         liable to You for damages, including any direct, indirect, special,
165         incidental, or consequential damages of any character arising as a
166         result of this License or out of the use or inability to use the
167         Work (including but not limited to damages for loss of goodwill,
168         work stoppage, computer failure or malfunction, or any and all
169         other commercial damages or losses), even if such Contributor
170         has been advised of the possibility of such damages.
171
172         9. Accepting Warranty or Additional Liability. While redistributing
173         the Work or Derivative Works thereof, You may choose to offer,
174         and charge a fee for, acceptance of support, warranty, indemnity,
175         or other liability obligations and/or rights consistent with this
176         License. However, in accepting such obligations, You may act only
177         on Your own behalf and on Your sole responsibility, not on behalf
178         of any other Contributor, and only if You agree to indemnify,
179         defend, and hold each Contributor harmless for any liability
180         incurred by, or claims asserted against, such Contributor by reason
181         of your accepting any such warranty or additional liability.
182
183         END OF TERMS AND CONDITIONS
184
185         APPENDIX: How to apply the Flora License to your work
186
187         To apply the Flora License to your work, attach the following
188         boilerplate notice, with the fields enclosed by brackets "[]"
189         replaced with your own identifying information. (Don't include
190         the brackets!) The text should be enclosed in the appropriate
191         comment syntax for the file format. We also recommend that a
192         file or class name and description of purpose be included on the
193         same "printed page" as the copyright notice for easier
194         identification within third-party archives.
195
196            Copyright [yyyy] [name of copyright owner]
197
198            Licensed under the Flora License, Version 1.1 (the "License");
199            you may not use this file except in compliance with the License.
200            You may obtain a copy of the License at
201
202                    http://floralicense.org/license/
203
204            Unless required by applicable law or agreed to in writing, software
205            distributed under the License is distributed on an "AS IS" BASIS,
206            WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
207            See the License for the specific language governing permissions and
208            limitations under the License.
209
210
211  * Authors: Hyunsook Park <hyunsook.park@samsung.com>
212  *                      Wonseop Kim <wonseop.kim@samsung.com>
213  */
214
215 /**
216  *      The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
217  *      To improve performance, the size of image(s) displayed on the screen should be a square(under
218  *      128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image
219  *      resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"
220  *      option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
221  *      So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve
222  *      launching time. A browser or web runtime engine should support "Web Storage" feature to use that
223  *      option.)
224  *
225  *      HTML Attributes:
226  *
227  *              data-thumbnail-cache : Determines whether to cache and resize images.
228  *
229  *      APIs:
230  *
231  *              next ( void )
232  *                      : This method moves each image forward one by one.
233  *              prev ( void )
234  *                      : This method moves each image backward one by one.
235  *              select ( [number] )
236  *                      : When the "select" method is called with an argument, the method selects the image of given index.
237  *                      If the method is called with no argument, it will return the Javascript object having "src"
238  *                      attribute having the selected image's URL.
239  *              add ( object or string [, number] )
240  *                      This method adds an image to Gallery3D widget.
241  *                      If the second argument isn't inputted, the image is added at the 0th position.
242  *              remove ( [number] )
243  *                      : This method deletes an image from Gallery3d widget.
244  *                      The argument defines the index of the image to be deleted.
245  *                      If an argument isn't inputted, it removes current image.
246  *              clearThumbnailCache ( void )
247  *                      : This method clears the cache data of all images when thumbnailCache option is set as 'true'.
248  *              refresh ( void )
249  *                      : This method updates and redraws current widget.
250  *              empty ( void )
251  *                      : This method removes all of images from Gallery3D widget.
252  *              length ( void )
253  *                      : This method gets the number of images.
254  *
255  *      Events:
256  *
257  *              select : Triggered when an image is selected.
258  *
259  *      Examples:
260  *
261  *              <script>
262  *                      $( "#gallery3d" ).on( "gallery3dcreate", function () {
263  *                              $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
264  *                      });
265  *              </script>
266  *              <div id="gallery3d" data-role="gallery3d"></div>
267  */
268
269 /**
270         @class Gallery3D
271         The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
272         <br/><br/>To add an gallery3d widget to the application, use the following code:
273
274                 <script>
275                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
276                                 $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
277                         });
278                 </script>
279                 <div id="gallery3d" data-role="gallery3d"></div>
280 */
281 /**
282         @property {Boolean} data-thumbnail-cache
283         Determines whether to cache and resize images.
284         To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).
285         "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
286         So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.
287         A browser or web runtime engine must support "Web Storage" feature to use this option.
288 */
289 /**
290         @event select
291         Triggered when an image is selected.
292
293                 <script>
294                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
295                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
296                                         .gallery3d( "add", { src: "2.jpg" } )
297                                         .gallery3d( "add", { src: "3.jpg" } );
298                         }).on( "select", function ( event, data, index ) {
299                                 // Handle the select event
300                                 var urlOfImage = data.src, indexOfImage = index;
301                         });
302                 </script>
303                 <div id="gallery3d" data-role="gallery3d"></div>
304 */
305 /**
306         @method next
307         This method moves each image forward one by one.
308
309                 <script>
310                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
311                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
312                                         .gallery3d( "add", { src: "2.jpg" } )
313                                         .gallery3d( "add", { src: "3.jpg" } )
314                                         .gallery3d( "next" );
315                         });
316                 </script>
317                 <div id="gallery3d" data-role="gallery3d"></div>
318 */
319 /**
320         @method prev
321         This method moves each image backward one by one.
322
323                 <script>
324                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
325                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
326                                         .gallery3d( "add", { src: "2.jpg" } )
327                                         .gallery3d( "add", { src: "3.jpg" } )
328                                         .gallery3d( "prev" );
329                         });
330                 </script>
331                 <div id="gallery3d" data-role="gallery3d"></div>
332 */
333 /**
334         @method select
335         When the "select" method is called with an argument, the method selects the image of given index.
336         If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image's URL.
337
338                 <script>
339                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
340                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
341                                         .gallery3d( "add", { src: "2.jpg" } )
342                                         .gallery3d( "add", { src: "3.jpg" } );
343                                 var selectedImage = $("#gallery3d"). gallery3d( "select" );
344                                 // selectedImage = { src: "3.jpg" };
345                         });
346                 </script>
347                 <div id="gallery3d" data-role="gallery3d"></div>
348 */
349 /**
350         @method add
351         This method adds an image to Gallery3D widget.
352         The first argument is a Javascript object having a "src" attribute or a string of image's path.
353         The second argument is an index of images.
354         If second argument isn't inputted, the image is added at the 0th position.
355
356                 <script>
357                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
358                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
359                                         .gallery3d( "add", "2.jpg", 1 );
360                         });
361                 </script>
362                 <div id="gallery3d" data-role="gallery3d"></div>
363 */
364 /**
365         @method remove
366         This method deletes an image from Gallery3d widget.
367         The argument defines the index of the image to be deleted.
368         If an argument isn't inputted, it removes current image.
369
370                 <script>
371                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
372                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
373                                         .gallery3d( "add", { src: "2.jpg" } )
374                                         .gallery3d( "add", { src: "3.jpg" } );
375
376                                 $( "#gallery3d" ).gallery3d( "remove" );
377                                 $( "#gallery3d" ).gallery3d( "remove", 1 );
378                         });
379                 </script>
380                 <div id="gallery3d" data-role="gallery3d"></div>
381 */
382 /**
383         @method clearThumbnailCache
384         This method clears the cache data of all images when thumbnailCache option is set as 'true'
385
386                 <script>
387                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
388                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
389                                         .gallery3d( "add", { src: "2.jpg" } )
390                                         .gallery3d( "add", { src: "3.jpg" } );
391
392                                 $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );
393                         });
394                 </script>
395                 <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>
396 */
397 /**
398         @method refresh
399         This method updates and redraws current widget.
400
401                 <script>
402                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
403                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
404                                         .gallery3d( "add", { src: "2.jpg" } )
405                                         .gallery3d( "add", { src: "3.jpg" } );
406
407                                 $( "#gallery3d" ).gallery3d( "refresh" );
408                         });
409                 </script>
410                 <div id="gallery3d" data-role="gallery3d"></div>
411 */
412 /**
413         @method empty
414         This method removes all of images from Gallery3D widget.
415
416                 <script>
417                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
418                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
419                                         .gallery3d( "add", { src: "2.jpg" } )
420                                         .gallery3d( "add", { src: "3.jpg" } );
421
422                                 $( "#gallery3d" ).gallery3d( "empty" );
423                         });
424                 </script>
425                 <div id="gallery3d" data-role="gallery3d"></div>
426 */
427 /**
428         @method length
429         This method gets the number of images.
430
431                 <script>
432                         $( "#gallery3d" ).on( "gallery3dcreate", function () {
433                                 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
434                                         .gallery3d( "add", { src: "2.jpg" } )
435                                         .gallery3d( "add", { src: "3.jpg" } );
436
437                                 var imagesLength = $( "#gallery3d" ).gallery3d( "length" );
438                                 // imagesLength = 3;
439                         });
440                 </script>
441                 <div id="gallery3d" data-role="gallery3d"></div>
442 */
443
444 ( function ( $, document, window, undefined ) {
445         function Node() {
446                 this.vertices = [
447                         -1.0, -1.0, 0.0,
448                         1.0, -1.0, 0.0,
449                         1.0,  1.0, 0.0,
450                         -1.0,  1.0, 0.0
451                 ];
452                 this.textureCoords = [
453                         1.0, 0.0,
454                         0.0, 0.0,
455                         0.0, 1.0,
456                         1.0, 1.0
457                 ];
458                 this.normalVectors = [
459                         0.0, 0.0, 1.0,
460                         0.0, 0.0, 1.0,
461                         0.0, 0.0, 1.0,
462                         0.0, 0.0, 1.0
463                 ];
464                 this.texture = null;
465                 this.textureBuffer = null;
466                 this.textureBufferItemSize = 0;
467                 this.mashOrder = [];
468                 this.mvMatrix = null;
469                 this.level = -1;
470                 this.targetLevel = 0;
471                 this.drawable = false;
472                 this.image = null;
473                 this.imageID = 0;
474         }
475
476         var isPreInitailization  = false,
477                 glMatrix = {},
478                 VERTEX_SHADER,
479                 FRAGMENT_SHADER,
480                 GlArray32,
481                 GlArray16,
482                 preInitialize = function () {
483                         if ( isPreInitailization ) {
484                                 return;
485                         }
486
487                         window.initGlMatrix( glMatrix );
488
489                         VERTEX_SHADER = [
490                                 "attribute vec3 aVertexPosition;",
491                                 "attribute vec2 aTextureCoord;",
492                                 "attribute vec3 aVertexNormal;",
493                                 "uniform mat4 uMoveMatrix;",
494                                 "uniform mat4 uPerspectiveMatrix;",
495                                 "uniform mat3 nNormalMatrix;",
496                                 "uniform vec3 uAmbientColor;",
497                                 "uniform vec3 uLightDirection;",
498                                 "uniform vec3 uDirectionColor;",
499                                 "uniform vec3 uLightDirection_first;",
500                                 "uniform vec3 uLightDirection_second;",
501                                 "varying vec2 vTextureCoord;",
502                                 "varying vec3 vLightWeight;",
503                                 "varying vec4 vFogWeight;",
504
505                                 "void main(void) {",
506                                 "       vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",
507                                 "       gl_Position = uPerspectiveMatrix * v_Position;",
508                                 "       vTextureCoord = aTextureCoord;",
509                                 "       float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",
510                                 "       vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.6, 1.0);",
511                                 "       vec3 transNormalVector = nNormalMatrix * aVertexNormal;",
512
513                                 "       float vLightWeightFirst = 0.0;",
514                                 "       float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",
515
516                                 "       vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",
517                                 "}"
518                         ].join( "\n" );
519
520                         FRAGMENT_SHADER = [
521                                 "precision mediump float;",
522                                 "varying vec2 vTextureCoord;",
523                                 "varying vec3 vLightWeight;",
524                                 "uniform sampler2D uSampler;",
525                                 "varying vec4 vFogWeight;",
526
527                                 "void main(void) {",
528                                 "       vec4 TextureColor;",
529                                 "       if ( vTextureCoord.s <= 0.01 || vTextureCoord.s >= 0.99 || vTextureCoord.t <= 0.01 || vTextureCoord.t >= 0.99 ) {",
530                                 "               TextureColor = vec4(1.0, 1.0, 1.0, 0.5);",
531                                 "       } else {",
532                                 "               TextureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));",
533                                 "       }",
534                                 "       TextureColor *= vFogWeight;",
535                                 "       gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",
536                                 "}"
537                         ].join( "\n" );
538
539                         GlArray32 = ( typeof window.Float32Array !== "undefined" ?
540                                         window.Float32Array :
541                                                 ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) );
542
543                         GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array );
544
545                         isPreInitailization = true;
546                 },
547                 degreeToRadian = function ( degree ) {
548                         return degree * Math.PI / 180;
549                 },
550                 getContext3D = function ( canvas ) {
551                         var gl, i,
552                                 contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];
553
554                         for ( i = 0; i < contextNames.length; i += 1 ) {
555                                 try {
556                                         gl = canvas.getContext( contextNames[i] );
557                                         if ( gl ) {
558                                                 break;
559                                         }
560                                 } catch ( e ) {
561                                         $( canvas ).html( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );
562                                         return;
563                                 }
564                         }
565                         return gl;
566                 },
567                 requestAnimationFrame = function ( callback ) {
568                         var id = window.setTimeout( callback, 1000 / 60 );
569                         return id;
570                 },
571                 cancelAnimationFrame = function ( id ) {
572                         window.clearTimeout( id );
573                 };
574
575         $.widget( "tizen.gallery3d", $.mobile.widget, {
576                 options: {
577                         thumbnailCache: false
578                 },
579
580                 _MAX_ITEM_COUNT: 28,
581                 _ANIMATION_END: 999,
582                 _DURATION_DEFAULT: 300,
583                 _DURATION_FIRST: 1600,
584                 _VIEWPORT_WIDTH: 1024,
585                 _VIEWPORT_HEIGHT: 456,
586                 _DIRECTION_LEFT: -1,
587                 _DIRECTION_RIGHT: +1,
588
589                 _gl: null,
590                 _shaderProgram : null,
591                 _positionBuffer : null,
592                 _textureCoordBuffer : null,
593                 _normalVectorBuffer : null,
594                 _nodes: null,
595                 _pMatrix : null,
596                 _animationID: 0,
597                 _dragInterval : 0,
598                 _startTime : 0,
599                 _sumTime : 0,
600                 _lightsPositionStack : [
601                         [0.0, 0.0, -1.0],       // back
602                         [-0.2, 0.0, 0.7]        // front
603                 ],
604                 _path: null,
605                 _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),
606                 _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),
607                 _canvas: null,
608                 _imageList: [],
609                 _maxDrawLength: 0,
610                 _firstImageNumber: 0,
611                 _lastImageNumber: 0,
612
613                 _create: function () {
614                         var self = this,
615                                 view = self.element,
616                                 option = self.options;
617
618                         preInitialize();
619
620                         self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );
621
622                         view.addClass( "ui-gallery3d" ).append( self._canvas );
623                         self._addBehavier();
624
625                         self._dragInterval = 1000 / 30; // 30fps
626
627                         $.each( self.options, function ( key, value ) {
628                                 self.options[ key ] = undefined;
629                                 self._setOption( key, value );
630                         });
631                 },
632
633                 destroy: function () {
634                         this._final();
635                         $.mobile.widget.prototype.destroy.call( this );
636                 },
637
638                 _setOption: function ( key, value ) {
639                         switch ( key ) {
640                         case "thumbnailCache" :
641                                 if ( typeof value === "string" ) {
642                                         value = ( value === "true" ) ? true : false;
643                                 } else {
644                                         value = !!value;
645                                 }
646                                 this._reset();
647                                 break;
648                         }
649
650                         $.mobile.widget.prototype._setOption.call( this, key, value );
651                 },
652
653                 _init: function ( canvas ) {
654                         var self = this,
655                                 pathPoints = [
656                                         [40, 0, -48],
657                                         [-12, 0, -40],  // contorl Point of Point1
658                                         [24, 0, -9],            // contorl Point of Point2
659                                         [-5, 0, -5]
660                                 ],
661                                 i;
662
663                         canvas = canvas || self._canvas;
664
665                         if ( !canvas ) {
666                                 return;
667                         }
668
669                         self._gl = self._gl || self._initGL( canvas[0] );
670                         if ( !self._gl ) {
671                                 return;
672                         }
673
674                         if ( !self._imageList ) {
675                                 return;
676                         }
677
678                         self._shaderProgram = self._shaderProgram || self._initShader( self._gl );
679                         if ( !self._shaderProgram ) {
680                                 return;
681                         }
682
683                         if ( self._imageList.length > self._MAX_ITEM_COUNT ) {
684                                 self._firstImageNumber = self._imageList.length - 1;
685                                 self._lastImageNumber = self._MAX_ITEM_COUNT - 1;
686                         }
687
688                         self._nodes = self._initBuffers( self._gl, self._shaderProgram );
689
690                         self._initTextures( self._gl, self._nodes );
691
692                         self._path = $.motionpath( "bezier2d", {
693                                 points: pathPoints,
694                                 maxLevel: self._MAX_ITEM_COUNT
695                         } );
696                         for ( i = 0; i < self._nodes.length; i += 1 ) {
697                                 self._path.levels[i] = self._path.levels[i + 1] || 0;
698                                 self._nodes[i].level = i;
699                         }
700                 },
701
702                 _final: function ( canvas ) {
703                         var self = this,
704                                 gl = self._gl;
705
706                         if ( !gl ) {
707                                 return;
708                         }
709
710                         self._stop();
711
712                         canvas = canvas || self._canvas;
713
714                         $( self._nodes ).each( function ( i ) {
715                                 var node = self._nodes[i];
716                                 gl.deleteTexture( node.texture );
717                                 node.texture = null;
718                         });
719                         self._nodes = null;
720
721                         gl.deleteBuffer( self._positionBuffer );
722                         self._positionBuffer = null;
723                         gl.deleteBuffer( self._textureCoordBuffer );
724                         self._textureCoordBuffer = null;
725                         gl.deleteBuffer( self._normalVectorBuffer );
726                         self._normalVectorBuffer = null;
727
728                         $.webgl.shader.deleteShaders( gl );
729                         gl.deleteProgram( self._shaderProgram );
730                         self._shaderProgram = null;
731
732                         self._gl = gl = null;
733                 },
734
735                 _addBehavier : function () {
736                         var self = this,
737                                 view = self.element,
738                                 canvas = self._canvas,
739                                 touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),
740                                 touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",
741                                 touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",
742                                 touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";
743
744                         canvas.on( "webglcontextlost", function ( e ) {
745                                 e.preventDefault();
746                         }).on( "webglcontextrestored", function ( e ) {
747                                 self._init();
748                         }).on( touchStartEvt, function ( e ) {
749                                 var i = 0,
750                                         startX = 0,
751                                         deltaMaxSteps = 20,
752                                         deltas = [ deltaMaxSteps ],
753                                         deltaTimes = [ deltaMaxSteps ],
754                                         deltaIndex = 0,
755                                         dragValue = 0,
756                                         dragDirection = false,
757                                         prevTime = 0;
758
759                                 e.preventDefault();
760                                 e.stopPropagation();
761
762                                 if ( self._imageList.length <= 1 ) {
763                                         return;
764                                 }
765
766                                 self._stop();
767
768                                 startX =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
769                                 prevTime = $.now();
770
771                                 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
772                                         deltas[i] = startX;
773                                         deltaTimes[i] = $.now();
774                                 }
775
776                                 deltaIndex += 1;
777
778                                 view.on( touchMoveEvt, function ( e ) {
779                                         var x, dx, interval;
780
781                                         e.preventDefault();
782                                         e.stopPropagation();
783
784                                         x =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
785                                         dx = startX - x;
786
787                                         deltas[deltaIndex] = x;
788                                         deltaTimes[deltaIndex] = $.now();
789                                         interval = deltaTimes[deltaIndex] - prevTime;
790
791                                         deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;
792
793                                         // Validation of drag
794                                         if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {
795                                                 if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {
796                                                         dragValue = 0;
797                                                         dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;
798                                                 }
799
800                                                 dragValue += Math.abs( dx ) / 100;
801                                                 if ( dragValue >= 1 ) {
802                                                         self._setPosition( self._ANIMATION_END, dragDirection );
803                                                         dragValue = 0;
804                                                 } else {
805                                                         self._setPosition( dragValue, dragDirection );
806                                                 }
807                                                 self._drawScene();
808                                                 startX = x;
809                                                 prevTime = $.now();
810                                         }
811                                 }).on( touchEndEvt, function ( e ) {
812                                         var baseTime = 0,
813                                                 recent = -1,
814                                                 index = 0,
815                                                 previous = 0,
816                                                 baseTimeRatio = 0,
817                                                 fx = 0,
818                                                 lastX = 0,
819                                                 velocityX = 0,
820                                                 dx = 0,
821                                                 isSwipe = true,
822                                                 direction;
823
824                                         e.preventDefault();
825                                         e.stopPropagation();
826
827                                         // Validation of swipe
828                                         baseTime = $.now() - self._swipeThresholdOfBasetimeGap;
829                                         lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
830                                         dx = startX - lastX;
831                                         startX = 0;
832                                         for ( i = 0; i < deltaMaxSteps; i += 1 ) {
833                                                 index = ( deltaIndex + i ) % deltaMaxSteps;
834                                                 if ( deltaTimes[index] > baseTime ) {
835                                                         recent = index;
836                                                         break;
837                                                 }
838                                         }
839                                         if ( recent < 0 ) {
840                                                 isSwipe = false;
841                                         }
842
843                                         if ( isSwipe ) {
844                                                 previous = recent;
845                                                 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
846                                                         previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;
847                                                         if ( deltaTimes[previous] < deltaTimes[recent] ) {
848                                                                 break;
849                                                         }
850                                                 }
851                                                 // too slow or too fast
852                                                 if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {
853                                                         isSwipe = false;
854                                                 }
855                                         }
856
857                                         if ( isSwipe ) {
858                                                 baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );
859                                                 fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];
860                                                 if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {
861                                                         fx = lastX;
862                                                 }
863                                                 velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );
864                                         }
865
866                                         if ( isSwipe && velocityX ) {
867                                                 direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;
868                                                 self._run( direction, Math.abs( velocityX ), dragValue );
869                                         } else if ( dragDirection !== 0 && dragValue ) {
870                                                 self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );
871                                         }
872
873                                         view.unbind( ".gallery3d" );
874                                 }).on( touchLeaveEvt, function ( e ) {
875                                         view.trigger( touchEndEvt );
876                                 });
877                         });
878                 },
879
880                 // ----------------------------------------------------------
881                 // WebGL
882                 // ----------------------------------------------------------
883                 _initGL: function ( canvas ) {
884                         var self = this,
885                                 mat4 = glMatrix.mat4,
886                                 gl;
887
888                         gl = getContext3D( canvas );
889                         if ( !gl ) {
890                                 return null;
891                         }
892
893                         gl.enable( gl.BLEND );
894                         gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
895
896                         gl.enable( gl.DEPTH_TEST );
897                         gl.depthFunc( gl.LEQUAL );
898
899                         canvas.width = self._VIEWPORT_WIDTH;
900                         canvas.height = self._VIEWPORT_HEIGHT;
901                         gl.viewportWidth = canvas.width;
902                         gl.viewportHeight = canvas.height;
903                         gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );
904                         self._pMatrix = mat4.create();
905                         mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );
906
907                         gl.clearColor( 0.15, 0.15, 0.15, 1.0 );
908                         gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
909
910                         return gl;
911                 },
912
913                 _initShader : function ( gl ) {
914                         var self = this,
915                                 shaderProgram;
916
917                         shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );
918                         gl.useProgram( shaderProgram );
919
920                         shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );
921                         gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );
922
923                         shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );
924                         gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );
925
926                         // Set light normal vectors for lighting~
927                         shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );
928                         gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );
929
930                         shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");
931                         shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");
932                         shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");
933
934                         // Set light variables~
935                         shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");
936                         shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");
937                         shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");
938                         shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");
939                         shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");
940
941                         return shaderProgram;
942                 },
943
944                 _initBuffers: function ( gl, shaderProgram ) {
945                         var self = this,
946                                 i = 0,
947                                 mashBase = 0,
948                                 vertices = [],
949                                 textureCoords = [],
950                                 normalVectors = [],
951                                 nodes = [],
952                                 maxDrawLength = self._MAX_ITEM_COUNT;
953
954                         for ( i = 0; i < self._imageList.length + 1; i += 1 ) {
955                                 nodes[i] = new Node();
956                                 $.merge( vertices, nodes[i].vertices );
957                                 $.merge( textureCoords, nodes[i].textureCoords );
958                                 $.merge( normalVectors, nodes[i].normalVectors );
959
960                                 nodes[i].textureBuffer = gl.createBuffer();
961                                 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );
962                                 mashBase = i * 4;
963                                 nodes[i].meshOrder = [
964                                         mashBase, mashBase + 1, mashBase + 2,
965                                         mashBase + 2, mashBase + 3, mashBase
966                                 ];
967                                 gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );
968                                 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory
969                                 nodes[i].textureBufferItemSize = 6;
970                         }
971
972                         self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );
973                         self._positionBuffer.itemSize = 3;
974
975                         self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );
976                         self._textureCoordBuffer.itemSize = 2;
977
978                         self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light
979                         self._normalVectorBuffer.itemSize = 3;
980
981                         // Ambient light
982                         gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );
983                         // Direcntion light
984                         gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );
985
986                         return nodes;
987                 },
988
989                 // ----------------------------------------------------------
990                 // Texture
991                 // ----------------------------------------------------------
992                 _initTextures: function ( gl, nodes ) {
993                         var self = this;
994
995                         $( nodes ).each( function ( i ) {
996                                 var node = nodes[i],
997                                         url;
998
999                                 if ( !self._imageList[i] ) {
1000                                         return false;
1001                                 }
1002
1003                                 url = self._imageList[i].src;
1004                                 node.texture = gl.createTexture();
1005                                 self._loadImage( url, i, i, gl, nodes );
1006                         });
1007                 },
1008
1009                 _loadImage: function ( url, i, imageID, gl, nodes ) {
1010                         var self = this,
1011                                 isMipmap = false,
1012                                 image,
1013                                 node;
1014
1015                         gl = gl || self._gl;
1016                         nodes = nodes || self._nodes;
1017                         isMipmap = isMipmap || false;
1018                         node = nodes[i];
1019                         node.image = node.image || new Image();
1020
1021                         $( node.image ).one( "load", function ( e ) {
1022                                 self._bindTexture( gl, node, this, isMipmap );
1023                                 node.imageID = imageID;
1024
1025                                 if ( !self._animationID ) {
1026                                         self._setPosition( 0, 0 );
1027                                 }
1028                         });
1029
1030                         if ( self.options.thumbnailCache ) {
1031                                 $.imageloader.getThumbnail( url, function ( result ) {
1032                                         if ( result === "NOT_FOUND_ERR" ) {
1033                                                 $.imageloader.setThumbnail( url, function ( result ) {
1034                                                         if ( result && result.length > 30 ) {
1035                                                                 node.image.src = result;
1036                                                                 isMipmap = true;
1037                                                         } else {
1038                                                                 node.image.src = url;
1039                                                         }
1040                                                 });
1041                                         } else if ( result && result.length > 30 ) {
1042                                                 node.image.src = result;
1043                                                 isMipmap = true;
1044                                         } else {
1045                                                 node.image.src = url;
1046                                         }
1047                                 });
1048                         } else {
1049                                 node.image.src = url;
1050                         }
1051                 },
1052
1053                 _bindTexture: function ( gl, node, image, isMipmap ) {
1054                         if ( !node || !node.texture ) {
1055                                 return;
1056                         }
1057
1058                         gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
1059
1060                         gl.bindTexture( gl.TEXTURE_2D, node.texture );
1061                         gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
1062
1063                         if ( isMipmap ) {
1064                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1065                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );
1066                                 gl.generateMipmap( gl.TEXTURE_2D );
1067                         } else {
1068                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1069                                 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
1070                         }
1071
1072                         gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
1073                         gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
1074
1075                         node.texture.loaded = true;
1076
1077                         // release texture memory
1078                         gl.bindTexture( gl.TEXTURE_2D, null );
1079                 },
1080
1081                 // ----------------------------------------------------------
1082                 // rendering
1083                 // ----------------------------------------------------------
1084                 _setPosition: function ( progress, direction ) {
1085                         var self = this,
1086                                 mat4 = glMatrix.mat4,
1087                                 nodes = self._nodes,
1088                                 imageList = self._imageList,
1089                                 imageListLength = imageList.length,
1090                                 itemCount = self._MAX_ITEM_COUNT,
1091                                 displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,
1092                                 nextLevelLenth = 0,
1093                                 i = 0,
1094                                 t = 0,
1095                                 position = 0,
1096                                 angle = 0,
1097                                 current = 0,
1098                                 next = 0,
1099                                 nextLevel = 0,
1100                                 path = self._path,
1101                                 nextImageID = 0;
1102
1103                         nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;
1104
1105                         if ( !nodes[i].level ) {
1106                                 nodes[i].level = displayLength;
1107                         }
1108
1109                         for ( i = 0; i < displayLength; i += 1 ) {
1110                                 if ( !nodes[i].mvMatrix ) {
1111                                         nodes[i].mvMatrix = mat4.create();
1112                                 }
1113
1114                                 if ( direction > 0 && nodes[i].level >= displayLength ) {
1115                                         nodes[i].level = 0;
1116                                 }
1117
1118                                 current = path.levels[nodes[i].level];
1119                                 nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;
1120                                 next = path.levels[nextLevel];
1121
1122                                 if ( imageListLength > itemCount ) {
1123                                         if ( direction > 0 && nextLevel === 1
1124                                                         && self._firstImageNumber !== nodes[i].imageID ) {
1125                                                 self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );
1126                                         } else if ( direction < 0 && nextLevel === nextLevelLenth - 1
1127                                                         && self._lastImageNumber !== nodes[i].imageID ) {
1128                                                 self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );
1129                                         }
1130                                 }
1131
1132                                 mat4.identity( nodes[i].mvMatrix );
1133                                 mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );
1134                                 mat4.rotate( nodes[i].mvMatrix, degreeToRadian( 19 ), [1, 0, 0] );
1135
1136                                 t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );
1137
1138                                 if ( progress >= self._ANIMATION_END ) {
1139                                         nodes[i].level = nextLevel || displayLength;
1140                                         t = path.levels[nodes[i].level];
1141                                 }
1142
1143                                 if ( ( progress < self._ANIMATION_END )
1144                                                 && ( direction <= 0 && nodes[i].level < 1 ) ) {
1145                                         nodes[i].drawable = false;
1146                                 } else {
1147                                         nodes[i].drawable = true;
1148                                 }
1149
1150                                 if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {
1151                                         self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );
1152                                 }
1153
1154                                 position = path.getPosition( t );
1155                                 angle = path.getAngle( t );
1156
1157                                 mat4.translate( nodes[i].mvMatrix, position );
1158                                 mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );
1159                         }
1160
1161                         if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {
1162                                 self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;
1163                                 if ( self._firstImageNumber < 0 ) {
1164                                         self._firstImageNumber = imageListLength - 1;
1165                                 }
1166
1167                                 self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;
1168                                 if ( self._lastImageNumber < 0 ) {
1169                                         self._lastImageNumber = imageListLength - 1;
1170                                 }
1171                         }
1172                         self._drawScene();
1173                 },
1174
1175                 _drawScene: function () {
1176                         if ( !this._gl || !this._shaderProgram ) {
1177                                 return;
1178                         }
1179
1180                         var self = this,
1181                                 gl = self._gl,
1182                                 shaderProgram = self._shaderProgram,
1183                                 nodes = self._nodes,
1184                                 nodesLength = nodes.length,
1185                                 i;
1186
1187                         gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
1188
1189                         gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );
1190                         gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1191
1192                         gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );
1193                         gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1194
1195                         gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );
1196                         gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1197
1198                         for ( i = 0; i < nodesLength; i += 1 ) {
1199                                 if ( nodes[i].drawable ) {
1200                                         self._drawElement( self._pMatrix, nodes[i] );
1201                                 }
1202                         }
1203                 },
1204
1205                 _drawElement: function ( perspectiveMatrix, targetNode ) {
1206                         var self = this,
1207                                 gl = self._gl,
1208                                 vec3 = glMatrix.vec3,
1209                                 mat3 = glMatrix.mat3,
1210                                 mat4 = glMatrix.mat4,
1211                                 shaderProgram = self._shaderProgram,
1212                                 moveMatrix = targetNode.mvMatrix,
1213                                 texture = targetNode.texture,
1214                                 meshIndexBuffer = targetNode.textureBuffer,
1215                                 meshIndexBufferItemSize = targetNode.textureBufferItemSize,
1216                                 lightPositions = self._lightsPositionStack,
1217                                 LightDir,
1218                                 normalMatrix;
1219
1220                         if ( !moveMatrix ) {
1221                                 return;
1222                         }
1223
1224                         gl.activeTexture( gl.TEXTURE0 );
1225                         if ( texture && texture.loaded ) {
1226                                 gl.bindTexture( gl.TEXTURE_2D, texture );
1227                         }
1228                         gl.uniform1i( shaderProgram.sampleUniform, 0 );
1229
1230                         LightDir = vec3.create();
1231                         vec3.normalize( lightPositions[0], LightDir );
1232                         vec3.scale( LightDir, -8 );
1233                         gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );
1234
1235                         vec3.normalize( lightPositions[1], LightDir );
1236                         vec3.scale( LightDir, -1 );
1237                         gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );
1238                         gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );
1239
1240                         gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );
1241                         gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );
1242
1243                         normalMatrix = mat3.create();
1244                         mat4.toInverseMat3( moveMatrix, normalMatrix );
1245                         mat3.transpose( normalMatrix );
1246                         gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );
1247
1248                         gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );
1249
1250                         // release buffer memory
1251                         gl.bindBuffer( gl.ARRAY_BUFFER, null );
1252                         gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
1253
1254                         // release texture memory
1255                         gl.bindTexture( gl.TEXTURE_2D, null );
1256                 },
1257
1258                 // ----------------------------------------------------------
1259                 // Animation
1260                 // ----------------------------------------------------------
1261                 _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {
1262                         var self = this,
1263                                 timeNow = $.now(),
1264                                 progress,
1265                                 removeCount = 0;
1266
1267                         easingType = easingType || "linear";
1268                         startValue = startValue || 0;
1269                         _removeCount = _removeCount || 0;
1270
1271                         if ( self._sumTime >= duration ) {
1272                                 self._setPosition( self._ANIMATION_END, direction );
1273                                 self._stop();
1274                                 return;
1275                         }
1276
1277                         if ( self._startTime === 0 ) {
1278                                 self._startTime = timeNow;
1279                         } else {
1280                                 self._sumTime = timeNow - self._startTime;
1281                                 progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );
1282                                 removeCount = parseInt( Math.abs( progress ), 10 );
1283
1284                                 if ( _removeCount !== removeCount ) {
1285                                         self._setPosition( self._ANIMATION_END, direction );
1286                                         _removeCount = removeCount;
1287
1288                                         if ( ( repeatCount - _removeCount ) >= 0 ) {
1289                                                 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1290                                         } else {
1291                                                 self._stop();
1292                                         }
1293                                         return;
1294                                 }
1295
1296                                 self._setPosition( progress - _removeCount, direction );
1297                         }
1298
1299                         self._animationID = requestAnimationFrame( function () {
1300                                 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1301                         });
1302                 },
1303
1304                 _run: function ( direction, repeatCount, startValue ) {
1305                         var self = this,
1306                                 repeat = repeatCount || 0,
1307                                 duration = self._DURATION_DEFAULT * ( repeat + 1 );
1308
1309                         if ( self._imageList.length <= 1 ) {
1310                                 return;
1311                         }
1312
1313                         startValue = startValue || 0;
1314                         duration = ( duration >= 0 ) ? duration : 0;
1315
1316                         if ( self._animationID ) {
1317                                 self._setPosition( self._ANIMATION_END, direction );
1318                                 self._stop();
1319                         }
1320
1321                         self._animate( "easeOutExpo", duration, direction, repeat, startValue );
1322                 },
1323
1324                 _reset: function () {
1325                         if ( !this._canvas || !this._gl ) {
1326                                 return;
1327                         }
1328
1329                         this._final();
1330                         this._init();
1331                         this.refresh();
1332                 },
1333
1334                 _stop: function () {
1335                         if ( this._animationID ) {
1336                                 cancelAnimationFrame( this._animationID );
1337                         }
1338                         this._animationID = 0;
1339
1340                         this._startTime = 0;
1341                         this._sumTime = 0;
1342                 },
1343
1344                 next: function () {
1345                         this._run( this._DIRECTION_LEFT , 0 );
1346                 },
1347
1348                 prev: function () {
1349                         this._run( this._DIRECTION_RIGHT, 0 );
1350                 },
1351
1352                 refresh: function () {
1353                         var view = this.element,
1354                                 canvas = view.find( "canvas.ui-gallery3d-canvas" );
1355
1356                         if ( canvas.width() !== view.width() ) {
1357                                 canvas.width( view.width() );
1358                         }
1359
1360                         if ( !this._animationID ) {
1361                                 this._setPosition( 0, 0 );
1362                         }
1363                 },
1364
1365                 select: function ( index ) {
1366                         var nodes = this._nodes,
1367                                 repeat,
1368                                 i,
1369                                 imageID,
1370                                 object = null,
1371                                 target = 0,
1372                                 direction = 0;
1373
1374                         if ( index && this._animationID ) {
1375                                 this._stop();
1376                         }
1377
1378                         for ( i in nodes ) {
1379                                 if ( nodes[i].level === 1 ) {
1380                                         object = this._imageList[ nodes[i].imageID ];
1381                                         imageID = nodes[i].imageID;
1382                                         break;
1383                                 }
1384                         }
1385
1386                         if ( !index ) {
1387                                 return object;
1388                         }
1389
1390                         if ( index < 0 && index >= this._imageList.length ) {
1391                                 return;
1392                         }
1393
1394                         target = index - imageID;
1395                         direction = ( target > 0 ) ? this._DIRECTION_LEFT
1396                                 : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );
1397                         if ( direction ) {
1398                                 this._run( direction, Math.abs( target ) - 1  );
1399                         }
1400                 },
1401
1402                 add: function ( item, index ) {
1403                         if ( !item ) {
1404                                 return;
1405                         }
1406
1407                         if ( typeof item === "string" ) {
1408                                 item = { "src" : item };
1409                         }
1410
1411                         index = index || 0;
1412                         if ( typeof index !== "number" && index < 0
1413                                         && index >= this._imageList.length ) {
1414                                 return;
1415                         }
1416
1417                         this._imageList.splice( index, 0, item );
1418                         if ( this._gl ) {
1419                                 this._reset();
1420                         }
1421                 },
1422
1423                 remove: function ( index ) {
1424                         index = index || 0;
1425                         if ( typeof index !== "number" && index < 0
1426                                         && index >= this._imageList.length ) {
1427                                 return;
1428                         }
1429
1430                         this._imageList.splice( index, 1 );
1431                         if ( this._gl ) {
1432                                 this._reset();
1433                         }
1434                 },
1435
1436                 clearThumbnailCache: function () {
1437                         if ( !this._nodes || ( this._nodes.length <= 0 ) ) {
1438                                 return;
1439                         }
1440
1441                         var i, url;
1442                         for ( i = 0; i < this._imageList.length; i += 1 ) {
1443                                 url = this._imageList[i].src;
1444                                 $.imageloader.removeThumbnail( url );
1445                         }
1446                 },
1447
1448                 empty: function () {
1449                         this._imageList = [];
1450                         this._reset();
1451                 },
1452
1453                 length: function () {
1454                         return this._imageList.length;
1455                 }
1456         });
1457
1458         $( document ).on( "pagecreate create", function ( e ) {
1459                 $( ":jqmData(role='gallery3d')" ).gallery3d();
1460         }).on( "pagechange", function ( e ) {
1461                 $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1462         });
1463
1464         $( window ).on( "resize orientationchange", function ( e ) {
1465                 $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1466         });
1467
1468 } ( jQuery, document, window ) );
1469