Gallery3d: Reduce loading time of widget
authorWonseop Kim <wonseop.kim@samsung.com>
Wed, 10 Apr 2013 06:46:27 +0000 (15:46 +0900)
committerYoumin Ha <youmin.ha@samsung.com>
Wed, 10 Apr 2013 14:55:13 +0000 (23:55 +0900)
Change the motion path from B-spline to Bezier2d.

Change-Id: Iab1163a5d51e6ed2e2e63179396a9dc5cf004cf8

src/js/widgets/components/motionpath.js
src/js/widgets/jquery.mobile.tizen.gallery3d.js

index 89799a4..edf8d44 100644 (file)
-//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
-//>>description: Tizen motion path component for gallery3d\r
-//>>label: Motion path\r
-//>>group: Tizen:Widgets:Components\r
-\r
-define( [ ], function ( ) {\r
-//>>excludeEnd("jqmBuildExclude");\r
-\r
-/* ***************************************************************************\r
- * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a\r
- * copy of this software and associated documentation files (the "Software"),\r
- * to deal in the Software without restriction, including without limitation\r
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
- * and/or sell copies of the Software, and to permit persons to whom the\r
- * Software is furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be included in\r
- * all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
- * DEALINGS IN THE SOFTWARE.\r
- * ***************************************************************************\r
- *\r
- * Authors: Hyunsook Park <hyunsook.park@samsung.com>\r
- *                     Wonseop Kim <wonseop.kim@samsung.com>\r
-*/\r
-\r
-( function ( $, window, undefined ) {\r
-       var HALF_PI = Math.PI / 2,\r
-               DEFAULT_STEP = 0.001,\r
-               MotionPath = {},\r
-               vec3 = window.vec3,\r
-               arcLength2d = function ( p0, p1 ) {\r
-                       var d = [ p1[0] - p0[0], p1[1] - p0[1] ],\r
-                               value = Math.sqrt( d[0] * d[0] + d[1] * d[1] );\r
-                       return value;\r
-               },\r
-               arcLength3d = function ( p0, p1 ) {\r
-                       var d = [ p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] ],\r
-                               value = Math.sqrt( d[0] * d[0] + d[1] * d[1] + d[2] * d[2] );\r
-                       return value;\r
-               };\r
-\r
-       MotionPath.base = function () {};\r
-       MotionPath.base.prototype = {\r
-               points: [],\r
-               step: DEFAULT_STEP,\r
-               length: 0,\r
-               levels: [],\r
-               init: function ( data ) {},\r
-               calculateLevel: function ( maxLevel ) {},\r
-               calculateTotalLength: function () {},\r
-               getPosition: function ( percent ) {},\r
-               getPercent: function ( start, interval ) {},\r
-               getAngle: function ( percent ) {}\r
-       };\r
-\r
-       MotionPath.bezier2d = function () {};\r
-       MotionPath.bezier2d.prototype = $.extend( true, {}, MotionPath.base.prototype, {\r
-               init: function ( data ) {\r
-                       this.points = data.points;\r
-                       this.step = data.step || DEFAULT_STEP;\r
-                       this.length = this.calculateTotalLength();\r
-                       this.levels = this.calculateLevel( data.maxLevel ) || [];\r
-               },\r
-\r
-               calculateLevel: function ( maxLevel ) {\r
-                       var totalLength = this.length,\r
-                               interval = totalLength / maxLevel,\r
-                               levels = [],\r
-                               i;\r
-\r
-                       if ( !maxLevel ) {\r
-                               return null;\r
-                       }\r
-\r
-                       for ( i = 0; i < maxLevel; i += 1 ) {\r
-                               levels[maxLevel - i] = this.getPercent( 0, interval * i );\r
-                       }\r
-\r
-                       return levels;\r
-               },\r
-\r
-               calculateTotalLength: function () {\r
-                       var step = this.step,\r
-                               current = this.getPosition( 0 ),\r
-                               last = current,\r
-                               length = 0,\r
-                               percent;\r
-                       for ( percent = step; percent <= 1; percent += step ) {\r
-                               current = this.getPosition( percent );\r
-                               length += arcLength2d( last, current );\r
-                               last = current;\r
-                       }\r
-                       return length;\r
-               },\r
-\r
-               getPosition: function ( percent ) {\r
-                       var points = this.points,\r
-                               getValue = function ( p1, c1, c2, p2, t ) {\r
-                                       return Math.pow(1 - t, 3) * p1 +\r
-                                               3 * t * Math.pow( 1 - t, 2 ) * c1 +\r
-                                               3 * Math.pow( t, 2 ) * ( 1 - t ) * c2 +\r
-                                               Math.pow( t, 3 ) * p2;\r
-                               },\r
-                               result = [\r
-                                       getValue( points[0][0], points[1][0], points[2][0], points[3][0], percent ),\r
-                                       getValue( points[0][1], points[1][1], points[2][1], points[3][1], percent )\r
-                               ];\r
-                       return result;\r
-               },\r
-\r
-               getPercent: function ( start, interval ) {\r
-                       var step = this.step,\r
-                               current = this.getPosition( start = start || 0 ),\r
-                               last = current,\r
-                               targetLength = start + interval,\r
-                               length = 0,\r
-                               percent;\r
-\r
-                       for ( percent = start + step; percent <= 1; percent += step ) {\r
-                               current = this.getPosition( percent );\r
-                               length += arcLength2d( last, current );\r
-                               if ( length >= targetLength ) {\r
-                                       return percent;\r
-                               }\r
-                               last = current;\r
-                       }\r
-                       return 1;\r
-               },\r
-\r
-               getAngle: function ( percent ) {\r
-                       var points = this.points,\r
-                               getTangent = function ( p1, c1, c2, p2, t ) {\r
-                                       return 3 * t * t * ( -p1 + 3 * c1 - 3 * c2 + p2 ) + 6 * t * ( p1 - 2 * c1 + c2 ) + 3 * ( -p1 + c1 );\r
-                               },\r
-                               tx = getTangent( points[0][0], points[1][0], points[2][0], points[3][0], percent ),\r
-                               ty = getTangent( points[0][1], points[1][1], points[2][1], points[3][1], percent );\r
-                       return Math.atan2( tx, ty ) - HALF_PI;\r
-               }\r
-\r
-       } );\r
-\r
-       // clamped cubic B-spline curve\r
-       // http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node17.html\r
-       // http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-coef.html\r
-       MotionPath.bspline = function () {};\r
-       MotionPath.bspline.prototype = $.extend( true, {}, MotionPath.base.prototype, {\r
-               _degree: 3,\r
-               _numberOfControls : 0,\r
-               _knotVectors: [],\r
-               _numberOfKnots: 0,\r
-\r
-               init: function ( data ) {\r
-                       this.points = data.points;\r
-                       this.step = data.step || DEFAULT_STEP;\r
-                       this._numberOfPoints = this.points.length - 1;\r
-                       this._numberOfKnots = this._numberOfPoints + this._degree + 1;\r
-\r
-                       var deltaKnot = 1 / ( this._numberOfKnots - ( 2 * this._degree ) ),\r
-                               v = deltaKnot,\r
-                               i = 0;\r
-\r
-                       while ( i <= this._numberOfKnots ) {\r
-                               if ( i <= this._degree ) {\r
-                                       this._knotVectors.push( 0 );\r
-                               } else if ( i < this._numberOfKnots - this._degree + 1 ) {\r
-                                       this._knotVectors.push( v );\r
-                                       v += deltaKnot;\r
-                               } else {\r
-                                       this._knotVectors.push( 1 );\r
-                               }\r
-                               i += 1;\r
-                       }\r
-\r
-                       this.length = this.calculateTotalLength();\r
-                       this.levels = this.calculateLevel( data.maxLevel ) || [];\r
-               },\r
-\r
-               _Np: function ( percent, i, degree ) {\r
-                       var knots = this._knotVectors,\r
-                               A = 0,\r
-                               B = 0,\r
-                               denominator = 0,\r
-                               N0 = function ( percent, i ) {\r
-                                       return ( ( knots[i] <= percent && percent < knots[i + 1] ) ? 1 : 0 );\r
-                               };\r
-\r
-                       if ( degree === 1 ) {\r
-                               A = N0( percent, i );\r
-                               B = N0( percent, i + 1 );\r
-                       } else {\r
-                               A = this._Np( percent, i, degree - 1 );\r
-                               B = this._Np( percent, i + 1, degree - 1 );\r
-                       }\r
-\r
-                       denominator = knots[i + degree] - knots[i];\r
-                       A *= ( denominator !== 0 ) ? ( ( percent - knots[i] ) / denominator ) : 0;\r
-                       denominator = knots[i + degree + 1] - knots[i + 1];\r
-                       B *= ( denominator !== 0 ) ? ( ( knots[i + degree + 1] - percent ) / denominator ) : 0;\r
-\r
-                       return A + B;\r
-               },\r
-\r
-               calculateLevel: function ( maxLevel ) {\r
-                       var totalLength = this.length,\r
-                               interval = totalLength / maxLevel,\r
-                               levels = [],\r
-                               i;\r
-\r
-                       if ( !maxLevel ) {\r
-                               return null;\r
-                       }\r
-\r
-                       for ( i = 0; i < maxLevel; i += 1 ) {\r
-                               levels[maxLevel - i] = this.getPercent( 0, interval * i );\r
-                       }\r
-                       return levels;\r
-               },\r
-\r
-               calculateTotalLength: function () {\r
-                       var step = this.step,\r
-                               current = this.getPosition( 0 ),\r
-                               last = current,\r
-                               length = 0,\r
-                               percent;\r
-                       for ( percent = step; percent <= 1; percent += step ) {\r
-                               current = this.getPosition( percent );\r
-                               length += arcLength3d( last, current );\r
-                               last = current;\r
-                       }\r
-                       return length;\r
-               },\r
-\r
-               getPosition: function ( percent ) {\r
-                       var result = [], i, j, sum;\r
-                       percent = percent.toFixed( 4 );\r
-                       for ( j = 0; j < 3; j += 1 ) {\r
-                               sum = 0;\r
-                               for ( i = 0; i <= this._numberOfPoints; i += 1 ) {\r
-                                       sum += this.points[i][j] * this._Np( percent, i, this._degree );\r
-                               }\r
-                               result[j] = sum;\r
-                       }\r
-\r
-                       return result;\r
-               },\r
-\r
-               getPercent: function ( start, interval ) {\r
-                       var step = this.step,\r
-                               current = this.getPosition( start = start || 0 ),\r
-                               last = current,\r
-                               targetLength = start + interval,\r
-                               length = 0,\r
-                               percent;\r
-\r
-                       for ( percent = start + step; percent <= 1; percent += step ) {\r
-                               current = this.getPosition( percent );\r
-                               length += arcLength3d( last, current );\r
-                               if ( length >= targetLength ) {\r
-                                       return percent;\r
-                               }\r
-                               last = current;\r
-                       }\r
-                       return 1;\r
-               },\r
-\r
-               getAngle: function ( percent ) {\r
-                       var prev = this.getPosition( percent ),\r
-                               next = this.getPosition( percent + 0.001 ),\r
-                               dir = vec3.normalize( vec3.direction( prev, next ) ),\r
-                               cosValue = vec3.dot( dir, [1, 0, 0] );\r
-\r
-                       return Math.acos( cosValue ) + Math.PI;\r
-               }\r
-       } );\r
-\r
-       $.motionpath = function ( type, data ) {\r
-               var object = new MotionPath[type]();\r
-               object.init( data );\r
-               return object;\r
-       };\r
-} ( jQuery, window ) );\r
-\r
-//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
-} );\r
-//>>excludeEnd("jqmBuildExclude");\r
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: Tizen motion path component for gallery3d
+//>>label: Motion path
+//>>group: Tizen:Widgets:Components
+
+define( [ ], function ( ) {
+//>>excludeEnd("jqmBuildExclude");
+
+/* ***************************************************************************
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * ***************************************************************************
+ *
+ * Authors: Hyunsook Park <hyunsook.park@samsung.com>
+ *                     Wonseop Kim <wonseop.kim@samsung.com>
+*/
+
+( function ( $, window, undefined ) {
+       var HALF_PI = Math.PI / 2,
+               DEFAULT_STEP = 0.001,
+               MotionPath = {},
+               vec3 = window.vec3,
+               arcLength3d = function ( p0, p1 ) {
+                       var d = [ p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2] ],
+                               value = Math.sqrt( d[0] * d[0] + d[1] * d[1] + d[2] * d[2] );
+                       return value;
+               };
+
+       MotionPath.base = function () {};
+       MotionPath.base.prototype = {
+               points: [],
+               step: DEFAULT_STEP,
+               length: 0,
+               levels: [],
+               init: function ( data ) {},
+               calculateLevel: function ( maxLevel ) {},
+               calculateTotalLength: function () {},
+               getPosition: function ( percent ) {},
+               getPercent: function ( start, interval ) {},
+               getAngle: function ( percent ) {}
+       };
+
+       MotionPath.bezier2d = function () {};
+       MotionPath.bezier2d.prototype = $.extend( true, {}, MotionPath.base.prototype, {
+               init: function ( data ) {
+                       this.points = data.points;
+                       this.step = data.step || DEFAULT_STEP;
+                       this.length = this.calculateTotalLength();
+                       this.levels = this.calculateLevel( data.maxLevel ) || [];
+               },
+
+               calculateLevel: function ( maxLevel ) {
+                       var totalLength = this.length,
+                               interval = totalLength / maxLevel,
+                               levels = [],
+                               i;
+
+                       if ( !maxLevel ) {
+                               return null;
+                       }
+
+                       for ( i = 0; i < maxLevel; i += 1 ) {
+                               levels[maxLevel - i] = this.getPercent( 0, interval * i );
+                       }
+
+                       return levels;
+               },
+
+               calculateTotalLength: function () {
+                       var step = this.step,
+                               current = this.getPosition( 0 ),
+                               last = current,
+                               length = 0,
+                               percent;
+                       for ( percent = step; percent <= 1; percent += step ) {
+                               current = this.getPosition( percent );
+                               length += arcLength3d( last, current );
+                               last = current;
+                       }
+                       return length;
+               },
+
+               getPosition: function ( percent ) {
+                       var points = this.points,
+                               getValue = function ( p1, c1, c2, p2, t ) {
+                                       return Math.pow(1 - t, 3) * p1 +
+                                               3 * t * Math.pow( 1 - t, 2 ) * c1 +
+                                               3 * Math.pow( t, 2 ) * ( 1 - t ) * c2 +
+                                               Math.pow( t, 3 ) * p2;
+                               },
+                               result = [
+                                       getValue( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
+                                       getValue( points[0][2], points[1][2], points[2][2], points[3][2], percent )
+                               ];
+                       return [ result[0], 0, result[1] ];
+               },
+
+               getPercent: function ( start, interval ) {
+                       var step = this.step,
+                               current = this.getPosition( start = start || 0 ),
+                               last = current,
+                               targetLength = start + interval,
+                               length = 0,
+                               percent;
+
+                       for ( percent = start + step; percent <= 1; percent += step ) {
+                               current = this.getPosition( percent );
+                               length += arcLength3d( last, current );
+                               if ( length >= targetLength ) {
+                                       return percent;
+                               }
+                               last = current;
+                       }
+                       return 1;
+               },
+
+               getAngle: function ( percent ) {
+                       var points = this.points,
+                               getTangent = function ( p1, c1, c2, p2, t ) {
+                                       return 3 * t * t * ( -p1 + 3 * c1 - 3 * c2 + p2 ) + 6 * t * ( p1 - 2 * c1 + c2 ) + 3 * ( -p1 + c1 );
+                               },
+                               tx = getTangent( points[0][0], points[1][0], points[2][0], points[3][0], percent ),
+                               ty = getTangent( points[0][2], points[1][2], points[2][2], points[3][2], percent );
+                       return Math.atan2( tx, ty ) - HALF_PI;
+               }
+
+       } );
+
+       // clamped cubic B-spline curve
+       // http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node17.html
+       // http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve-coef.html
+       MotionPath.bspline = function () {};
+       MotionPath.bspline.prototype = $.extend( true, {}, MotionPath.base.prototype, {
+               _degree: 3,
+               _numberOfControls : 0,
+               _knotVectors: [],
+               _numberOfKnots: 0,
+
+               init: function ( data ) {
+                       this.points = data.points;
+                       this.step = data.step || DEFAULT_STEP;
+                       this._numberOfPoints = this.points.length - 1;
+                       this._numberOfKnots = this._numberOfPoints + this._degree + 1;
+
+                       var deltaKnot = 1 / ( this._numberOfKnots - ( 2 * this._degree ) ),
+                               v = deltaKnot,
+                               i = 0;
+
+                       while ( i <= this._numberOfKnots ) {
+                               if ( i <= this._degree ) {
+                                       this._knotVectors.push( 0 );
+                               } else if ( i < this._numberOfKnots - this._degree + 1 ) {
+                                       this._knotVectors.push( v );
+                                       v += deltaKnot;
+                               } else {
+                                       this._knotVectors.push( 1 );
+                               }
+                               i += 1;
+                       }
+
+                       this.length = this.calculateTotalLength();
+                       this.levels = this.calculateLevel( data.maxLevel ) || [];
+               },
+
+               _Np: function ( percent, i, degree ) {
+                       var knots = this._knotVectors,
+                               A = 0,
+                               B = 0,
+                               denominator = 0,
+                               N0 = function ( percent, i ) {
+                                       return ( ( knots[i] <= percent && percent < knots[i + 1] ) ? 1 : 0 );
+                               };
+
+                       if ( degree === 1 ) {
+                               A = N0( percent, i );
+                               B = N0( percent, i + 1 );
+                       } else {
+                               A = this._Np( percent, i, degree - 1 );
+                               B = this._Np( percent, i + 1, degree - 1 );
+                       }
+
+                       denominator = knots[i + degree] - knots[i];
+                       A *= ( denominator !== 0 ) ? ( ( percent - knots[i] ) / denominator ) : 0;
+                       denominator = knots[i + degree + 1] - knots[i + 1];
+                       B *= ( denominator !== 0 ) ? ( ( knots[i + degree + 1] - percent ) / denominator ) : 0;
+
+                       return A + B;
+               },
+
+               calculateLevel: function ( maxLevel ) {
+                       var totalLength = this.length,
+                               interval = totalLength / maxLevel,
+                               levels = [],
+                               i;
+
+                       if ( !maxLevel ) {
+                               return null;
+                       }
+
+                       for ( i = 0; i < maxLevel; i += 1 ) {
+                               levels[maxLevel - i] = this.getPercent( 0, interval * i );
+                       }
+                       return levels;
+               },
+
+               calculateTotalLength: function () {
+                       var step = this.step,
+                               current = this.getPosition( 0 ),
+                               last = current,
+                               length = 0,
+                               percent;
+                       for ( percent = step; percent <= 1; percent += step ) {
+                               current = this.getPosition( percent );
+                               length += arcLength3d( last, current );
+                               last = current;
+                       }
+                       return length;
+               },
+
+               getPosition: function ( percent ) {
+                       var result = [], i, j, sum;
+                       percent = percent.toFixed( 4 );
+                       for ( j = 0; j < 3; j += 1 ) {
+                               sum = 0;
+                               for ( i = 0; i <= this._numberOfPoints; i += 1 ) {
+                                       sum += this.points[i][j] * this._Np( percent, i, this._degree );
+                               }
+                               result[j] = sum;
+                       }
+
+                       return result;
+               },
+
+               getPercent: function ( start, interval ) {
+                       var step = this.step,
+                               current = this.getPosition( start = start || 0 ),
+                               last = current,
+                               targetLength = start + interval,
+                               length = 0,
+                               percent;
+
+                       for ( percent = start + step; percent <= 1; percent += step ) {
+                               current = this.getPosition( percent );
+                               length += arcLength3d( last, current );
+                               if ( length >= targetLength ) {
+                                       return percent;
+                               }
+                               last = current;
+                       }
+                       return 1;
+               },
+
+               getAngle: function ( percent ) {
+                       var prev = this.getPosition( percent ),
+                               next = this.getPosition( percent + 0.001 ),
+                               dir = vec3.normalize( vec3.direction( prev, next ) ),
+                               cosValue = vec3.dot( dir, [1, 0, 0] );
+
+                       return Math.acos( cosValue ) + Math.PI;
+               }
+       } );
+
+       $.motionpath = function ( type, data ) {
+               var object = new MotionPath[type]();
+               object.init( data );
+               return object;
+       };
+} ( jQuery, window ) );
+
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+} );
+//>>excludeEnd("jqmBuildExclude");
index 6d66ac2..4034ed5 100644 (file)
-//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
-//>>description: 3D photo gallery widget.\r
-//>>label: Gallery3d\r
-//>>group: Tizen:Widgets\r
-\r
-define( [ "components/imageloader", "components/motionpath", "components/webgl" ], function ( ) {\r
-//>>excludeEnd("jqmBuildExclude");\r
-\r
-\r
-/* ***************************************************************************\r
- * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.\r
- *\r
- * Permission is hereby granted, free of charge, to any person obtaining a\r
- * copy of this software and associated documentation files (the "Software"),\r
- * to deal in the Software without restriction, including without limitation\r
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
- * and/or sell copies of the Software, and to permit persons to whom the\r
- * Software is furnished to do so, subject to the following conditions:\r
- *\r
- * The above copyright notice and this permission notice shall be included in\r
- * all copies or substantial portions of the Software.\r
- *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\r
- * DEALINGS IN THE SOFTWARE.\r
- * ***************************************************************************\r
- *\r
- * Authors: Hyunsook Park <hyunsook.park@samsung.com>\r
- *                     Wonseop Kim <wonseop.kim@samsung.com>\r
- */\r
-\r
-/**\r
- *     'Gallery3D' is a 3D photo gallery widget.\r
- *     Images are arranged with a S-shaped curve on a 3-dimensional coordinate system.\r
- *     A user can rotate images by swiping the widget area.\r
- *     To improve performance, the size of image(s) displayed on the screen should be a square(under\r
- *     128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image\r
- *     resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"\r
- *     option resizes the gallery images under 128x128 pixels and stores the images on a local storage.\r
- *     So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve\r
- *     launching time. A browser or web runtime engine should support "Web Storage" feature to use that\r
- *     option.)\r
- *\r
- *     HTML Attributes:\r
- *\r
- *             data-thumbnail-cache : Determines whether to cache and resize images.\r
- *\r
- *     APIs:\r
- *\r
- *             next ( void )\r
- *                     : This method moves each image forward one by one.\r
- *             prev ( void )\r
- *                     : This method moves each image backward one by one.\r
- *             select ( [number] )\r
- *                     : When the "select" method is called with an argument, the method selects the image of given index.\r
- *                     If the method is called with no argument, it will return the Javascript object having "src"\r
- *                     attribute having the selected image’s URL.\r
- *             add ( object or string [, number] )\r
- *                     This method adds an image to Gallery3D widget.\r
- *                     If the second argument isn't inputted, the image is added at the 0th position.\r
- *             remove ( [number] )\r
- *                     : This method deletes an image from Gallery3d widget.\r
- *                     The argument defines the index of the image to be deleted.\r
- *                     If an argument isn't inputted, it removes current image.\r
- *             clearThumbnailCache ( void )\r
- *                     : This method clears the cache data of all images when thumbnailCache option is set as 'true'.\r
- *             refresh ( void )\r
- *                     : This method updates and redraws current widget.\r
- *             empty ( void )\r
- *                     : This method removes all of images from Gallery3D widget.\r
- *             length ( void )\r
- *                     : This method gets the number of images.\r
- *\r
- *     Events:\r
- *\r
- *             select : Triggered when an image is selected.\r
- *\r
- *     Examples:\r
- *\r
- *             <script>\r
- *                     $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
- *                             $( "#gallery3d" ).gallery3d( "add", "01.jpg" );\r
- *                     });\r
- *             </script>\r
- *             <div id="gallery3d" data-role="gallery3d"></div>\r
- */\r
-\r
-/**\r
-       @class Gallery3D\r
-       The gallery3d widget is a 3D photo gallery widget.\r
-       Images are arranged with a S-shaped curve on a 3-dimensional coordinate system.\r
-       A user can rotate images by swiping the widget area.\r
-       <br/><br/>To add an gallery3d widget to the application, use the following code:\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", "01.jpg" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @property {Boolean} data-thumbnail-cache\r
-       Determines whether to cache and resize images.\r
-       To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).\r
-       "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.\r
-       So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.\r
-       A browser or web runtime engine must support "Web Storage" feature to use this option.\r
-*/\r
-/**\r
-       @event select\r
-       Triggered when an image is selected.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-                       }).on( "select", function ( event, data, index ) {\r
-                               // Handle the select event\r
-                               var urlOfImage = data.src, indexOfImage = index;\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method next\r
-       This method moves each image forward one by one.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } )\r
-                                       .gallery3d( "next" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method prev\r
-       This method moves each image backward one by one.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } )\r
-                                       .gallery3d( "prev" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method select\r
-       When the "select" method is called with an argument, the method selects the image of given index.\r
-       If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image’s URL.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-                               var selectedImage = $("#gallery3d"). gallery3d( "select" );\r
-                               // selectedImage = { src: "3.jpg" };\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method add\r
-       This method adds an image to Gallery3D widget.\r
-       The first argument is a Javascript object having a "src" attribute or a string of image's path.\r
-       The second argument is an index of images.\r
-       If second argument isn't inputted, the image is added at the 0th position.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", "2.jpg", 1 );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method remove\r
-       This method deletes an image from Gallery3d widget.\r
-       The argument defines the index of the image to be deleted.\r
-       If an argument isn't inputted, it removes current image.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-\r
-                               $( "#gallery3d" ).gallery3d( "remove" );\r
-                               $( "#gallery3d" ).gallery3d( "remove", 1 );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method clearThumbnailCache\r
-       This method clears the cache data of all images when thumbnailCache option is set as 'true'\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-\r
-                               $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>\r
-*/\r
-/**\r
-       @method refresh\r
-       This method updates and redraws current widget.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-\r
-                               $( "#gallery3d" ).gallery3d( "refresh" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method empty\r
-       This method removes all of images from Gallery3D widget.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-\r
-                               $( "#gallery3d" ).gallery3d( "empty" );\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-/**\r
-       @method length\r
-       This method gets the number of images.\r
-\r
-               <script>\r
-                       $( "#gallery3d" ).on( "gallery3dcreate", function () {\r
-                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )\r
-                                       .gallery3d( "add", { src: "2.jpg" } )\r
-                                       .gallery3d( "add", { src: "3.jpg" } );\r
-\r
-                               var imagesLength = $( "#gallery3d" ).gallery3d( "length" );\r
-                               // imagesLength = 3;\r
-                       });\r
-               </script>\r
-               <div id="gallery3d" data-role="gallery3d"></div>\r
-*/\r
-\r
-( function ( $, document, window, undefined ) {\r
-       window.requestAnimationFrame = ( function () {\r
-               return function ( callback ) {\r
-                       var id = window.setTimeout( callback, 1000 / 60 );\r
-                       return id;\r
-               };\r
-       } () );\r
-\r
-       window.cancelAnimationFrame = ( function () {\r
-               return function ( id ) {\r
-                       window.clearTimeout( id );\r
-               };\r
-       } () );\r
-\r
-       var vec3 = window.vec3,\r
-               mat3 = window.mat3,\r
-               mat4 = window.mat4,\r
-               GlArray32 = ( typeof window.Float32Array !== "undefined" ? window.Float32Array : ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) ),\r
-               GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array ),\r
-               getContext3D = function ( canvas ) {\r
-                       var gl, i,\r
-                               contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];\r
-\r
-                       for ( i = 0; i < contextNames.length; i += 1 ) {\r
-                               try {\r
-                                       gl = canvas.getContext( contextNames[i] );\r
-                                       if ( gl ) {\r
-                                               break;\r
-                                       }\r
-                               } catch ( e ) {\r
-                                       window.alert( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );\r
-                                       return;\r
-                               }\r
-                       }\r
-                       return gl;\r
-               },\r
-               VERTEX_SHADER = [\r
-                       "attribute vec3 aVertexPosition;",\r
-                       "attribute vec2 aTextureCoord;",\r
-                       "attribute vec3 aVertexNormal;",\r
-                       "uniform mat4 uMoveMatrix;",\r
-                       "uniform mat4 uPerspectiveMatrix;",\r
-                       "uniform mat3 nNormalMatrix;",\r
-                       "uniform vec3 uAmbientColor;",\r
-                       "uniform vec3 uLightDirection;",\r
-                       "uniform vec3 uDirectionColor;",\r
-                       "uniform vec3 uLightDirection_first;",\r
-                       "uniform vec3 uLightDirection_second;",\r
-                       "varying vec2 vTextureCoord;",\r
-                       "varying vec3 vLightWeight;",\r
-                       "varying vec4 vFogWeight;",\r
-\r
-                       "void main(void) {",\r
-                       "       vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",\r
-                       "       gl_Position = uPerspectiveMatrix * v_Position;",\r
-                       "       vTextureCoord = aTextureCoord;",\r
-                       "       float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",\r
-                       "       vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.0, 1.0);",\r
-                       "       vec3 transNormalVector = nNormalMatrix * aVertexNormal;",\r
-\r
-                       "       float vLightWeightFirst = 0.0;",\r
-                       "       float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",\r
-\r
-                       "       vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",\r
-                       "}"\r
-               ].join( "\n" ),\r
-               FRAGMENT_SHADER = [\r
-                       "precision mediump float;",\r
-                       "varying vec2 vTextureCoord;",\r
-                       "varying vec3 vLightWeight;",\r
-                       "uniform sampler2D uSampler;",\r
-                       "varying vec4 vFogWeight;",\r
-\r
-                       "void main(void) {",\r
-                       "       vec4 TextureColor = (texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t))) * vFogWeight;",\r
-                       "       gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",\r
-                       "}"\r
-               ].join( "\n" );\r
-\r
-       function Node() {\r
-               this.vertices = [\r
-                       -1.0, -1.0, 0.0,\r
-                       1.0, -1.0, 0.0,\r
-                       1.0,  1.0, 0.0,\r
-                       -1.0,  1.0, 0.0\r
-               ];\r
-               this.textureCoords = [\r
-                       1.0, 0.0,\r
-                       0.0, 0.0,\r
-                       0.0, 1.0,\r
-                       1.0, 1.0\r
-               ];\r
-               this.normalVectors = [\r
-                       0.0, 0.0, 1.0,\r
-                       0.0, 0.0, 1.0,\r
-                       0.0, 0.0, 1.0,\r
-                       0.0, 0.0, 1.0\r
-               ];\r
-               this.texture = null;\r
-               this.textureBuffer = null;\r
-               this.textureBufferItemSize = 0;\r
-               this.mashOrder = [];\r
-               this.mvMatrix = null;\r
-               this.level = -1;\r
-               this.targetLevel = 0;\r
-               this.drawable = false;\r
-               this.image = null;\r
-               this.imageID = 0;\r
-       }\r
-\r
-       $.widget( "tizen.gallery3d", $.mobile.widget, {\r
-               options: {\r
-                       thumbnailCache: false\r
-               },\r
-\r
-               _MAX_ITEM_COUNT: 28,\r
-               _ANIMATION_END: 999,\r
-               _DURATION_DEFAULT: 300,\r
-               _DURATION_FIRST: 1600,\r
-               _VIEWPORT_WIDTH: 1024,\r
-               _VIEWPORT_HEIGHT: 456,\r
-               _DIRECTION_LEFT: -1,\r
-               _DIRECTION_RIGHT: +1,\r
-\r
-               _gl: null,\r
-               _shaderProgram : null,\r
-               _positionBuffer : null,\r
-               _textureCoordBuffer : null,\r
-               _normalVectorBuffer : null,\r
-               _nodes: null,\r
-               _pMatrix : null,\r
-               _animationID: 0,\r
-               _dragInterval : 0,\r
-               _startTime : 0,\r
-               _sumTime : 0,\r
-               _lightsPositionStack : [\r
-                       [0.0, 0.0, -1.0],       // back\r
-                       [-0.2, 0.0, 0.7]        // front\r
-               ],\r
-               _path: null,\r
-               _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),\r
-               _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),\r
-               _canvas: null,\r
-               _imageList: [],\r
-               _maxDrawLength: 0,\r
-               _firstImageNumber: 0,\r
-               _lastImageNumber: 0,\r
-\r
-               _create: function () {\r
-                       var self = this,\r
-                               view = self.element,\r
-                               option = self.options;\r
-\r
-                       self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );\r
-\r
-                       view.addClass( "ui-gallery3d" ).append( self._canvas );\r
-                       self._addBehavier();\r
-\r
-                       self._dragInterval = 1000 / 30; // 30fps\r
-\r
-                       $.each( self.options, function ( key, value ) {\r
-                               self.options[ key ] = undefined;\r
-                               self._setOption( key, value );\r
-                       });\r
-\r
-               },\r
-\r
-               _setOption: function ( key, value ) {\r
-                       switch ( key ) {\r
-                       case "thumbnailCache" :\r
-                               if ( typeof value === "string" ) {\r
-                                       value = ( value === "true" ) ? true : false;\r
-                               } else {\r
-                                       value = !!value;\r
-                               }\r
-                               this._reset();\r
-                               break;\r
-                       }\r
-\r
-                       $.mobile.widget.prototype._setOption.call( this, key, value );\r
-               },\r
-\r
-               _init: function ( canvas ) {\r
-                       var self = this,\r
-                               pathPoints = [\r
-                                       [40, 0, -48],\r
-                                       [-12, 0, -40],  // contorl Point of Point1\r
-                                       [24, 0, -9],            // contorl Point of Point2\r
-                                       [-5, 0, -5]\r
-                               ],\r
-                               i;\r
-\r
-                       canvas = canvas || self._canvas;\r
-\r
-                       if ( !canvas ) {\r
-                               return;\r
-                       }\r
-\r
-                       self._gl = self._gl || self._initGL( canvas[0] );\r
-                       if ( !self._gl ) {\r
-                               return;\r
-                       }\r
-\r
-                       if ( !self._imageList ) {\r
-                               return;\r
-                       }\r
-\r
-                       self._shaderProgram = self._shaderProgram || self._initShader( self._gl );\r
-                       if ( !self._shaderProgram ) {\r
-                               return;\r
-                       }\r
-\r
-                       if ( self._imageList.length > self._MAX_ITEM_COUNT ) {\r
-                               self._firstImageNumber = self._imageList.length - 1;\r
-                               self._lastImageNumber = self._MAX_ITEM_COUNT - 1;\r
-                       }\r
-\r
-                       self._nodes = self._initBuffers( self._gl, self._shaderProgram );\r
-                       self._initTextures( self._gl, self._nodes );\r
-                       self._path = $.motionpath( "bspline", {\r
-                               points: pathPoints,\r
-                               maxLevel: self._MAX_ITEM_COUNT\r
-                       } );\r
-                       for ( i = 0; i < self._nodes.length; i += 1 ) {\r
-                               self._path.levels[i] = self._path.levels[i + 1] || 0;\r
-                               self._nodes[i].level = i;\r
-                       }\r
-               },\r
-\r
-               _final: function ( canvas ) {\r
-                       var self = this,\r
-                               gl = self._gl;\r
-\r
-                       if ( !gl ) {\r
-                               return;\r
-                       }\r
-\r
-                       canvas = canvas || self._canvas;\r
-\r
-                       $( self._nodes ).each( function ( i ) {\r
-                               var node = self._nodes[i];\r
-                               gl.deleteTexture( node.texture );\r
-                               node.texture = null;\r
-                       });\r
-                       self._nodes = null;\r
-\r
-                       gl.deleteBuffer( self._positionBuffer );\r
-                       self._positionBuffer = null;\r
-                       gl.deleteBuffer( self._textureCoordBuffer );\r
-                       self._textureCoordBuffer = null;\r
-                       gl.deleteBuffer( self._normalVectorBuffer );\r
-                       self._normalVectorBuffer = null;\r
-\r
-                       $.webgl.shader.deleteShaders( gl );\r
-                       gl.deleteProgram( self._shaderProgram );\r
-                       self._shaderProgram = null;\r
-\r
-                       self._gl = gl = null;\r
-               },\r
-\r
-               _addBehavier : function () {\r
-                       var self = this,\r
-                               view = self.element,\r
-                               canvas = self._canvas,\r
-                               touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),\r
-                               touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",\r
-                               touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",\r
-                               touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";\r
-\r
-                       $( document ).unbind( ".gallery3d" ).bind( "pagechange.gallery3d", function ( e ) {\r
-                               $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );\r
-                       }).bind( "pageremove.gallery3d", function ( e ) {\r
-                               $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );\r
-                       });\r
-\r
-                       $( window ).unbind( ".gallery3d" ).bind( "resize.gallery3d orientationchange.gallery3d", function ( e ) {\r
-                               $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );\r
-                       }).bind( "unload.gallery3d", function ( e ) {\r
-                               $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );\r
-                       });\r
-\r
-                       view.bind( "_destory", function ( e ) {\r
-                               self._final();\r
-                       });\r
-\r
-                       canvas.bind( "webglcontextlost", function ( e ) {\r
-                               e.preventDefault();\r
-                       }).bind( "webglcontextrestored", function ( e ) {\r
-                               self._init();\r
-                       }).bind( touchStartEvt, function ( e ) {\r
-                               var i = 0,\r
-                                       startX = 0,\r
-                                       deltaMaxSteps = 20,\r
-                                       deltas = [ deltaMaxSteps ],\r
-                                       deltaTimes = [ deltaMaxSteps ],\r
-                                       deltaIndex = 0,\r
-                                       dragValue = 0,\r
-                                       dragDirection = false,\r
-                                       prevTime = 0;\r
-\r
-                               e.preventDefault();\r
-                               e.stopPropagation();\r
-\r
-                               if ( self._imageList.length <= 1 ) {\r
-                                       return;\r
-                               }\r
-\r
-                               self._stop();\r
-\r
-                               startX =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
-                               prevTime = $.now();\r
-\r
-                               for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
-                                       deltas[i] = startX;\r
-                                       deltaTimes[i] = $.now();\r
-                               }\r
-\r
-                               deltaIndex += 1;\r
-\r
-                               view.bind( touchMoveEvt, function ( e ) {\r
-                                       var x, dx, interval;\r
-\r
-                                       e.preventDefault();\r
-                                       e.stopPropagation();\r
-\r
-                                       x =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
-                                       dx = startX - x;\r
-\r
-                                       deltas[deltaIndex] = x;\r
-                                       deltaTimes[deltaIndex] = $.now();\r
-                                       interval = deltaTimes[deltaIndex] - prevTime;\r
-\r
-                                       deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;\r
-\r
-                                       // Validation of drag\r
-                                       if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {\r
-                                               if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {\r
-                                                       dragValue = 0;\r
-                                                       dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;\r
-                                               }\r
-\r
-                                               dragValue += Math.abs( dx ) / 100;\r
-                                               if ( dragValue >= 1 ) {\r
-                                                       self._setPosition( self._ANIMATION_END, dragDirection );\r
-                                                       dragValue = 0;\r
-                                               } else {\r
-                                                       self._setPosition( dragValue, dragDirection );\r
-                                               }\r
-                                               self._drawScene();\r
-                                               startX = x;\r
-                                               prevTime = $.now();\r
-                                       }\r
-                               }).bind( touchEndEvt, function ( e ) {\r
-                                       var baseTime = 0,\r
-                                               recent = -1,\r
-                                               index = 0,\r
-                                               previous = 0,\r
-                                               baseTimeRatio = 0,\r
-                                               fx = 0,\r
-                                               lastX = 0,\r
-                                               velocityX = 0,\r
-                                               dx = 0,\r
-                                               isSwipe = true,\r
-                                               direction;\r
-\r
-                                       e.preventDefault();\r
-                                       e.stopPropagation();\r
-\r
-                                       // Validation of swipe\r
-                                       baseTime = $.now() - self._swipeThresholdOfBasetimeGap;\r
-                                       lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;\r
-                                       dx = startX - lastX;\r
-                                       startX = 0;\r
-                                       for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
-                                               index = ( deltaIndex + i ) % deltaMaxSteps;\r
-                                               if ( deltaTimes[index] > baseTime ) {\r
-                                                       recent = index;\r
-                                                       break;\r
-                                               }\r
-                                       }\r
-                                       if ( recent < 0 ) {\r
-                                               isSwipe = false;\r
-                                       }\r
-\r
-                                       if ( isSwipe ) {\r
-                                               previous = recent;\r
-                                               for ( i = 0; i < deltaMaxSteps; i += 1 ) {\r
-                                                       previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;\r
-                                                       if ( deltaTimes[previous] < deltaTimes[recent] ) {\r
-                                                               break;\r
-                                                       }\r
-                                               }\r
-                                               // too slow or too fast\r
-                                               if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {\r
-                                                       isSwipe = false;\r
-                                               }\r
-                                       }\r
-\r
-                                       if ( isSwipe ) {\r
-                                               baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );\r
-                                               fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];\r
-                                               if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {\r
-                                                       fx = lastX;\r
-                                               }\r
-                                               velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );\r
-                                       }\r
-\r
-                                       if ( isSwipe && velocityX ) {\r
-                                               direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;\r
-                                               self._run( direction, Math.abs( velocityX ), dragValue );\r
-                                       } else if ( dragDirection !== 0 && dragValue ) {\r
-                                               self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );\r
-                                       }\r
-\r
-                                       view.unbind( ".gallery3d" );\r
-                               }).bind( touchLeaveEvt, function ( e ) {\r
-                                       view.trigger( touchEndEvt );\r
-                               });\r
-                       });\r
-               },\r
-\r
-               // ----------------------------------------------------------\r
-               // Data parsing\r
-               // ----------------------------------------------------------\r
-               _loadData: function ( jsonUrl, key ) {\r
-                       var self = this;\r
-\r
-                       $.ajax({\r
-                               async : false,\r
-                               url : jsonUrl,\r
-                               dataType: "json",\r
-                               success : function ( data ) {\r
-                                       self._imageList = $.extend( [], data[ key ] );\r
-                               }\r
-                       });\r
-               },\r
-\r
-               // ----------------------------------------------------------\r
-               // WebGL\r
-               // ----------------------------------------------------------\r
-               _initGL: function ( canvas ) {\r
-                       var self = this,\r
-                               gl;\r
-\r
-                       gl = getContext3D( canvas );\r
-                       if ( !gl ) {\r
-                               window.alert( "There's no WebGL context available!!!" );\r
-                               return null;\r
-                       }\r
-\r
-                       gl.enable( gl.BLEND );\r
-                       gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );\r
-\r
-                       gl.enable( gl.DEPTH_TEST );\r
-                       gl.depthFunc( gl.LEQUAL );\r
-\r
-                       canvas.width = self._VIEWPORT_WIDTH;\r
-                       canvas.height = self._VIEWPORT_HEIGHT;\r
-                       gl.viewportWidth = canvas.width;\r
-                       gl.viewportHeight = canvas.height;\r
-                       gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );\r
-                       self._pMatrix = mat4.create();\r
-                       mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );\r
-\r
-                       gl.clearColor( 0.0, 0.0, 0.0, 1.0 );\r
-                       gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );\r
-\r
-                       return gl;\r
-               },\r
-\r
-               _initShader : function ( gl ) {\r
-                       var self = this,\r
-                               shaderProgram;\r
-\r
-                       shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );\r
-                       gl.useProgram( shaderProgram );\r
-\r
-                       shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );\r
-                       gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );\r
-\r
-                       shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );\r
-                       gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );\r
-\r
-                       // Set light normal vectors for lighting~\r
-                       shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );\r
-                       gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );\r
-\r
-                       shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");\r
-                       shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");\r
-                       shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");\r
-\r
-                       // Set light variables~\r
-                       shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");\r
-                       shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");\r
-                       shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");\r
-                       shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");\r
-                       shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");\r
-\r
-                       return shaderProgram;\r
-               },\r
-\r
-               _initBuffers: function ( gl, shaderProgram ) {\r
-                       var self = this,\r
-                               i = 0,\r
-                               mashBase = 0,\r
-                               vertices = [],\r
-                               textureCoords = [],\r
-                               normalVectors = [],\r
-                               nodes = [],\r
-                               maxDrawLength = self._MAX_ITEM_COUNT;\r
-\r
-                       for ( i = 0; i < self._imageList.length + 1; i += 1 ) {\r
-                               nodes[i] = new Node();\r
-                               $.merge( vertices, nodes[i].vertices );\r
-                               $.merge( textureCoords, nodes[i].textureCoords );\r
-                               $.merge( normalVectors, nodes[i].normalVectors );\r
-\r
-                               nodes[i].textureBuffer = gl.createBuffer();\r
-                               gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );\r
-                               mashBase = i * 4;\r
-                               nodes[i].meshOrder = [\r
-                                       mashBase, mashBase + 1, mashBase + 2,\r
-                                       mashBase + 2, mashBase + 3, mashBase\r
-                               ];\r
-                               gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );\r
-                               gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory\r
-                               nodes[i].textureBufferItemSize = 6;\r
-                       }\r
-\r
-                       self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );\r
-                       self._positionBuffer.itemSize = 3;\r
-\r
-                       self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );\r
-                       self._textureCoordBuffer.itemSize = 2;\r
-\r
-                       self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light\r
-                       self._normalVectorBuffer.itemSize = 3;\r
-\r
-                       // Ambient light\r
-                       gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );\r
-                       // Direcntion light\r
-                       gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );\r
-\r
-                       return nodes;\r
-               },\r
-\r
-               // ----------------------------------------------------------\r
-               // Texture\r
-               // ----------------------------------------------------------\r
-               _initTextures: function ( gl, nodes ) {\r
-                       var self = this;\r
-\r
-                       $( nodes ).each( function ( i ) {\r
-                               var node = nodes[i],\r
-                                       url;\r
-\r
-                               if ( !self._imageList[i] ) {\r
-                                       return false;\r
-                               }\r
-\r
-                               url = self._imageList[i].src;\r
-                               node.texture = gl.createTexture();\r
-                               self._loadImage( url, i, i, gl, nodes );\r
-                       });\r
-               },\r
-\r
-               _loadImage: function ( url, i, imageID, gl, nodes ) {\r
-                       var self = this,\r
-                               isMipmap = false,\r
-                               image,\r
-                               node;\r
-\r
-                       gl = gl || self._gl;\r
-                       nodes = nodes || self._nodes;\r
-                       isMipmap = isMipmap || false;\r
-                       node = nodes[i];\r
-                       node.image = node.image || new Image();\r
-\r
-                       $( node.image ).one( "load", function ( e ) {\r
-                               self._bindTexture( gl, node, this, isMipmap );\r
-                               node.imageID = imageID;\r
-\r
-                               if ( !self._animationID ) {\r
-                                       self._setPosition( 0, 0 );\r
-                               }\r
-                       });\r
-\r
-                       if ( self.options.thumbnailCache ) {\r
-                               $.imageloader.getThumbnail( url, function ( result ) {\r
-                                       if ( result === "NOT_FOUND_ERR" ) {\r
-                                               $.imageloader.setThumbnail( url, function ( result ) {\r
-                                                       if ( result && result.length > 30 ) {\r
-                                                               node.image.src = result;\r
-                                                               isMipmap = true;\r
-                                                       } else {\r
-                                                               node.image.src = url;\r
-                                                       }\r
-                                               });\r
-                                       } else if ( result && result.length > 30 ) {\r
-                                               node.image.src = result;\r
-                                               isMipmap = true;\r
-                                       } else {\r
-                                               node.image.src = url;\r
-                                       }\r
-                               });\r
-                       } else {\r
-                               node.image.src = url;\r
-                       }\r
-               },\r
-\r
-               _bindTexture: function ( gl, node, image, isMipmap ) {\r
-                       if ( !node || !node.texture ) {\r
-                               return;\r
-                       }\r
-\r
-                       gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );\r
-\r
-                       gl.bindTexture( gl.TEXTURE_2D, node.texture );\r
-                       gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );\r
-\r
-                       if ( isMipmap ) {\r
-                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\r
-                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );\r
-                               gl.generateMipmap( gl.TEXTURE_2D );\r
-                       } else {\r
-                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );\r
-                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );\r
-                       }\r
-\r
-                       gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );\r
-                       gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );\r
-\r
-                       node.texture.loaded = true;\r
-\r
-                       // release texture memory\r
-                       gl.bindTexture( gl.TEXTURE_2D, null );\r
-               },\r
-\r
-               // ----------------------------------------------------------\r
-               // rendering\r
-               // ----------------------------------------------------------\r
-               _setPosition: function ( progress, direction ) {\r
-                       var self = this,\r
-                               nodes = self._nodes,\r
-                               imageList = self._imageList,\r
-                               imageListLength = imageList.length,\r
-                               itemCount = self._MAX_ITEM_COUNT,\r
-                               displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,\r
-                               nextLevelLenth = 0,\r
-                               i = 0,\r
-                               t = 0,\r
-                               position = 0,\r
-                               angle = 0,\r
-                               current = 0,\r
-                               next = 0,\r
-                               nextLevel = 0,\r
-                               path = self._path,\r
-                               nextImageID = 0;\r
-\r
-                       nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;\r
-\r
-                       if ( !nodes[i].level ) {\r
-                               nodes[i].level = displayLength;\r
-                       }\r
-\r
-                       for ( i = 0; i < displayLength; i += 1 ) {\r
-                               if ( !nodes[i].mvMatrix ) {\r
-                                       nodes[i].mvMatrix = mat4.create();\r
-                               }\r
-\r
-                               if ( direction > 0 && nodes[i].level >= displayLength ) {\r
-                                       nodes[i].level = 0;\r
-                               }\r
-\r
-                               current = path.levels[nodes[i].level];\r
-                               nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;\r
-                               next = path.levels[nextLevel];\r
-\r
-                               if ( imageListLength > itemCount ) {\r
-                                       if ( direction > 0 && nextLevel === 1\r
-                                                       && self._firstImageNumber !== nodes[i].imageID ) {\r
-                                               self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );\r
-                                       } else if ( direction < 0 && nextLevel === nextLevelLenth - 1\r
-                                                       && self._lastImageNumber !== nodes[i].imageID ) {\r
-                                               self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );\r
-                                       }\r
-                               }\r
-\r
-                               mat4.identity( nodes[i].mvMatrix );\r
-                               mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );\r
-                               mat4.rotate( nodes[i].mvMatrix, self._degreeToRadian( 19 ), [1, 0, 0] );\r
-\r
-                               t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );\r
-\r
-                               if ( progress >= self._ANIMATION_END ) {\r
-                                       nodes[i].level = nextLevel || displayLength;\r
-                                       t = path.levels[nodes[i].level];\r
-                               }\r
-\r
-                               if ( ( progress < self._ANIMATION_END )\r
-                                               && ( direction <= 0 && nodes[i].level < 1 ) ) {\r
-                                       nodes[i].drawable = false;\r
-                               } else {\r
-                                       nodes[i].drawable = true;\r
-                               }\r
-\r
-                               if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {\r
-                                       self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );\r
-                               }\r
-\r
-                               position = path.getPosition( t );\r
-                               angle = path.getAngle( t );\r
-\r
-                               mat4.translate( nodes[i].mvMatrix, position );\r
-                               mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );\r
-                       }\r
-\r
-                       if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {\r
-                               self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;\r
-                               if ( self._firstImageNumber < 0 ) {\r
-                                       self._firstImageNumber = imageListLength - 1;\r
-                               }\r
-\r
-                               self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;\r
-                               if ( self._lastImageNumber < 0 ) {\r
-                                       self._lastImageNumber = imageListLength - 1;\r
-                               }\r
-                       }\r
-                       self._drawScene();\r
-               },\r
-\r
-               _drawScene: function () {\r
-                       if ( !this._gl || !this._shaderProgram ) {\r
-                               return;\r
-                       }\r
-\r
-                       var self = this,\r
-                               gl = self._gl,\r
-                               shaderProgram = self._shaderProgram,\r
-                               nodes = self._nodes,\r
-                               nodesLength = nodes.length,\r
-                               i;\r
-\r
-                       gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );\r
-\r
-                       gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );\r
-                       gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
-\r
-                       gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );\r
-                       gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
-\r
-                       gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );\r
-                       gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );\r
-\r
-                       for ( i = 0; i < nodesLength; i += 1 ) {\r
-                               if ( nodes[i].drawable ) {\r
-                                       self._drawElement( self._pMatrix, nodes[i] );\r
-                               }\r
-                       }\r
-               },\r
-\r
-               _drawElement: function ( perspectiveMatrix, targetNode ) {\r
-                       var self = this,\r
-                               gl = self._gl,\r
-                               shaderProgram = self._shaderProgram,\r
-                               moveMatrix = targetNode.mvMatrix,\r
-                               texture = targetNode.texture,\r
-                               meshIndexBuffer = targetNode.textureBuffer,\r
-                               meshIndexBufferItemSize = targetNode.textureBufferItemSize,\r
-                               lightPositions = self._lightsPositionStack,\r
-                               LightDir,\r
-                               normalMatrix;\r
-\r
-                       if ( !moveMatrix ) {\r
-                               return;\r
-                       }\r
-\r
-                       gl.activeTexture( gl.TEXTURE0 );\r
-                       if ( texture && texture.loaded ) {\r
-                               gl.bindTexture( gl.TEXTURE_2D, texture );\r
-                       }\r
-                       gl.uniform1i( shaderProgram.sampleUniform, 0 );\r
-\r
-                       LightDir = vec3.create();\r
-                       vec3.normalize( lightPositions[0], LightDir );\r
-                       vec3.scale( LightDir, -8 );\r
-                       gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );\r
-\r
-                       vec3.normalize( lightPositions[1], LightDir );\r
-                       vec3.scale( LightDir, -1 );\r
-                       gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );\r
-                       gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );\r
-\r
-                       gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );\r
-                       gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );\r
-\r
-                       normalMatrix = mat3.create();\r
-                       mat4.toInverseMat3( moveMatrix, normalMatrix );\r
-                       mat3.transpose( normalMatrix );\r
-                       gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );\r
-\r
-                       gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );\r
-\r
-                       // release buffer memory\r
-                       gl.bindBuffer( gl.ARRAY_BUFFER, null );\r
-                       gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );\r
-\r
-                       // release texture memory\r
-                       gl.bindTexture( gl.TEXTURE_2D, null );\r
-               },\r
-\r
-               // ----------------------------------------------------------\r
-               // Animation\r
-               // ----------------------------------------------------------\r
-               _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {\r
-                       var self = this,\r
-                               timeNow = $.now(),\r
-                               progress,\r
-                               removeCount = 0;\r
-\r
-                       easingType = easingType || "linear";\r
-                       startValue = startValue || 0;\r
-                       _removeCount = _removeCount || 0;\r
-\r
-                       if ( self._sumTime >= duration ) {\r
-                               self._setPosition( self._ANIMATION_END, direction );\r
-                               self._stop();\r
-                               return;\r
-                       }\r
-\r
-                       if ( self._startTime === 0 ) {\r
-                               self._startTime = timeNow;\r
-                       } else {\r
-                               self._sumTime = timeNow - self._startTime;\r
-                               progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );\r
-                               removeCount = parseInt( Math.abs( progress ), 10 );\r
-\r
-                               if ( _removeCount !== removeCount ) {\r
-                                       self._setPosition( self._ANIMATION_END, direction );\r
-                                       _removeCount = removeCount;\r
-\r
-                                       if ( ( repeatCount - _removeCount ) >= 0 ) {\r
-                                               self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );\r
-                                       } else {\r
-                                               self._stop();\r
-                                       }\r
-                                       return;\r
-                               }\r
-\r
-                               self._setPosition( progress - _removeCount, direction );\r
-                       }\r
-\r
-                       self._animationID = window.requestAnimationFrame( function () {\r
-                               self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );\r
-                       });\r
-               },\r
-\r
-               _run: function ( direction, repeatCount, startValue ) {\r
-                       var self = this,\r
-                               repeat = repeatCount || 0,\r
-                               duration = self._DURATION_DEFAULT * ( repeat + 1 );\r
-\r
-                       if ( self._imageList.length <= 1 ) {\r
-                               return;\r
-                       }\r
-\r
-                       startValue = startValue || 0;\r
-                       duration = ( duration >= 0 ) ? duration : 0;\r
-\r
-                       if ( self._animationID ) {\r
-                               self._setPosition( self._ANIMATION_END, direction );\r
-                               self._stop();\r
-                       }\r
-\r
-                       self._animate( "easeOutExpo", duration, direction, repeat, startValue );\r
-               },\r
-\r
-               _reset: function () {\r
-                       if ( !this._canvas || !this._gl ) {\r
-                               return;\r
-                       }\r
-\r
-                       this._final();\r
-                       this._init();\r
-                       this.refresh();\r
-               },\r
-\r
-               _stop: function () {\r
-                       if ( this._animationID ) {\r
-                               window.cancelAnimationFrame( this._animationID );\r
-                       }\r
-                       this._animationID = 0;\r
-\r
-                       this._startTime = 0;\r
-                       this._sumTime = 0;\r
-               },\r
-\r
-               _degreeToRadian: function ( degree ) {\r
-                       return degree * Math.PI / 180;\r
-               },\r
-\r
-               next: function () {\r
-                       this._run( this._DIRECTION_LEFT , 0 );\r
-               },\r
-\r
-               prev: function () {\r
-                       this._run( this._DIRECTION_RIGHT, 0 );\r
-               },\r
-\r
-               refresh: function () {\r
-                       var view = this.element,\r
-                               canvas = view.find( "canvas.ui-gallery3d-canvas" );\r
-\r
-                       if ( canvas.width() !== view.width() ) {\r
-                               canvas.width( view.width() );\r
-                       }\r
-\r
-                       if ( !this._animationID ) {\r
-                               this._setPosition( 0, 0 );\r
-                       }\r
-               },\r
-\r
-               select: function ( index ) {\r
-                       var nodes = this._nodes,\r
-                               repeat,\r
-                               i,\r
-                               imageID,\r
-                               object = null,\r
-                               target = 0,\r
-                               direction = 0;\r
-\r
-                       if ( index && this._animationID ) {\r
-                               this._stop();\r
-                       }\r
-\r
-                       for ( i in nodes ) {\r
-                               if ( nodes[i].level === 1 ) {\r
-                                       object = this._imageList[ nodes[i].imageID ];\r
-                                       imageID = nodes[i].imageID;\r
-                                       break;\r
-                               }\r
-                       }\r
-\r
-                       if ( !index ) {\r
-                               return object;\r
-                       }\r
-\r
-                       if ( index < 0 && index >= this._imageList.length ) {\r
-                               return;\r
-                       }\r
-\r
-                       target = index - imageID;\r
-                       direction = ( target > 0 ) ? this._DIRECTION_LEFT\r
-                               : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );\r
-                       if ( direction ) {\r
-                               this._run( direction, Math.abs( target ) - 1  );\r
-                       }\r
-               },\r
-\r
-               add: function ( item, index ) {\r
-                       if ( !item ) {\r
-                               return;\r
-                       }\r
-\r
-                       if ( typeof item === "string" ) {\r
-                               item = { "src" : item };\r
-                       }\r
-\r
-                       index = index || 0;\r
-                       if ( typeof index !== "number" && index < 0\r
-                                       && index >= this._imageList.length ) {\r
-                               return;\r
-                       }\r
-\r
-                       this._imageList.splice( index, 0, item );\r
-                       if ( this._gl ) {\r
-                               this._reset();\r
-                       }\r
-               },\r
-\r
-               remove: function ( index ) {\r
-                       index = index || 0;\r
-                       if ( typeof index !== "number" && index < 0\r
-                                       && index >= this._imageList.length ) {\r
-                               return;\r
-                       }\r
-\r
-                       this._imageList.splice( index, 1 );\r
-                       if ( this._gl ) {\r
-                               this._reset();\r
-                       }\r
-               },\r
-\r
-               clearThumbnailCache: function () {\r
-                       if ( !this._nodes || ( this._nodes.length <= 0 ) ) {\r
-                               return;\r
-                       }\r
-\r
-                       var i, url;\r
-                       for ( i = 0; i < this._imageList.length; i += 1 ) {\r
-                               url = this._imageList[i].src;\r
-                               $.imageloader.removeThumbnail( url );\r
-                       }\r
-               },\r
-\r
-               empty: function () {\r
-                       this._imageList = [];\r
-                       this._reset();\r
-               },\r
-\r
-               length: function () {\r
-                       return this._imageList.length;\r
-               }\r
-       });\r
-\r
-       $( document ).bind( "pagecreate create", function ( e ) {\r
-               $( ":jqmData(role='gallery3d')" ).gallery3d();\r
-       });\r
-\r
-} ( jQuery, document, window ) );\r
-\r
-//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);\r
-} );\r
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+//>>description: 3D photo gallery widget.
+//>>label: Gallery3d
+//>>group: Tizen:Widgets
+
+define( [ "components/imageloader", "components/motionpath", "components/webgl" ], function ( ) {
+//>>excludeEnd("jqmBuildExclude");
+
+
+/* ***************************************************************************
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * ***************************************************************************
+ *
+ * Authors: Hyunsook Park <hyunsook.park@samsung.com>
+ *                     Wonseop Kim <wonseop.kim@samsung.com>
+ */
+
+/**
+ *     The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
+ *     To improve performance, the size of image(s) displayed on the screen should be a square(under
+ *     128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image
+ *     resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"
+ *     option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
+ *     So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve
+ *     launching time. A browser or web runtime engine should support "Web Storage" feature to use that
+ *     option.)
+ *
+ *     HTML Attributes:
+ *
+ *             data-thumbnail-cache : Determines whether to cache and resize images.
+ *
+ *     APIs:
+ *
+ *             next ( void )
+ *                     : This method moves each image forward one by one.
+ *             prev ( void )
+ *                     : This method moves each image backward one by one.
+ *             select ( [number] )
+ *                     : When the "select" method is called with an argument, the method selects the image of given index.
+ *                     If the method is called with no argument, it will return the Javascript object having "src"
+ *                     attribute having the selected image’s URL.
+ *             add ( object or string [, number] )
+ *                     This method adds an image to Gallery3D widget.
+ *                     If the second argument isn't inputted, the image is added at the 0th position.
+ *             remove ( [number] )
+ *                     : This method deletes an image from Gallery3d widget.
+ *                     The argument defines the index of the image to be deleted.
+ *                     If an argument isn't inputted, it removes current image.
+ *             clearThumbnailCache ( void )
+ *                     : This method clears the cache data of all images when thumbnailCache option is set as 'true'.
+ *             refresh ( void )
+ *                     : This method updates and redraws current widget.
+ *             empty ( void )
+ *                     : This method removes all of images from Gallery3D widget.
+ *             length ( void )
+ *                     : This method gets the number of images.
+ *
+ *     Events:
+ *
+ *             select : Triggered when an image is selected.
+ *
+ *     Examples:
+ *
+ *             <script>
+ *                     $( "#gallery3d" ).on( "gallery3dcreate", function () {
+ *                             $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
+ *                     });
+ *             </script>
+ *             <div id="gallery3d" data-role="gallery3d"></div>
+ */
+
+/**
+       @class Gallery3D
+       The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
+       <br/><br/>To add an gallery3d widget to the application, use the following code:
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @property {Boolean} data-thumbnail-cache
+       Determines whether to cache and resize images.
+       To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).
+       "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
+       So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.
+       A browser or web runtime engine must support "Web Storage" feature to use this option.
+*/
+/**
+       @event select
+       Triggered when an image is selected.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+                       }).on( "select", function ( event, data, index ) {
+                               // Handle the select event
+                               var urlOfImage = data.src, indexOfImage = index;
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method next
+       This method moves each image forward one by one.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } )
+                                       .gallery3d( "next" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method prev
+       This method moves each image backward one by one.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } )
+                                       .gallery3d( "prev" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method select
+       When the "select" method is called with an argument, the method selects the image of given index.
+       If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image’s URL.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+                               var selectedImage = $("#gallery3d"). gallery3d( "select" );
+                               // selectedImage = { src: "3.jpg" };
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method add
+       This method adds an image to Gallery3D widget.
+       The first argument is a Javascript object having a "src" attribute or a string of image's path.
+       The second argument is an index of images.
+       If second argument isn't inputted, the image is added at the 0th position.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", "2.jpg", 1 );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method remove
+       This method deletes an image from Gallery3d widget.
+       The argument defines the index of the image to be deleted.
+       If an argument isn't inputted, it removes current image.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+
+                               $( "#gallery3d" ).gallery3d( "remove" );
+                               $( "#gallery3d" ).gallery3d( "remove", 1 );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method clearThumbnailCache
+       This method clears the cache data of all images when thumbnailCache option is set as 'true'
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+
+                               $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>
+*/
+/**
+       @method refresh
+       This method updates and redraws current widget.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+
+                               $( "#gallery3d" ).gallery3d( "refresh" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method empty
+       This method removes all of images from Gallery3D widget.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+
+                               $( "#gallery3d" ).gallery3d( "empty" );
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+/**
+       @method length
+       This method gets the number of images.
+
+               <script>
+                       $( "#gallery3d" ).on( "gallery3dcreate", function () {
+                               $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
+                                       .gallery3d( "add", { src: "2.jpg" } )
+                                       .gallery3d( "add", { src: "3.jpg" } );
+
+                               var imagesLength = $( "#gallery3d" ).gallery3d( "length" );
+                               // imagesLength = 3;
+                       });
+               </script>
+               <div id="gallery3d" data-role="gallery3d"></div>
+*/
+
+( function ( $, document, window, undefined ) {
+       window.requestAnimationFrame = ( function () {
+               return function ( callback ) {
+                       var id = window.setTimeout( callback, 1000 / 60 );
+                       return id;
+               };
+       } () );
+
+       window.cancelAnimationFrame = ( function () {
+               return function ( id ) {
+                       window.clearTimeout( id );
+               };
+       } () );
+
+       var vec3 = window.vec3,
+               mat3 = window.mat3,
+               mat4 = window.mat4,
+               GlArray32 = ( typeof window.Float32Array !== "undefined" ? window.Float32Array : ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) ),
+               GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array ),
+               getContext3D = function ( canvas ) {
+                       var gl, i,
+                               contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];
+
+                       for ( i = 0; i < contextNames.length; i += 1 ) {
+                               try {
+                                       gl = canvas.getContext( contextNames[i] );
+                                       if ( gl ) {
+                                               break;
+                                       }
+                               } catch ( e ) {
+                                       window.alert( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );
+                                       return;
+                               }
+                       }
+                       return gl;
+               },
+               VERTEX_SHADER = [
+                       "attribute vec3 aVertexPosition;",
+                       "attribute vec2 aTextureCoord;",
+                       "attribute vec3 aVertexNormal;",
+                       "uniform mat4 uMoveMatrix;",
+                       "uniform mat4 uPerspectiveMatrix;",
+                       "uniform mat3 nNormalMatrix;",
+                       "uniform vec3 uAmbientColor;",
+                       "uniform vec3 uLightDirection;",
+                       "uniform vec3 uDirectionColor;",
+                       "uniform vec3 uLightDirection_first;",
+                       "uniform vec3 uLightDirection_second;",
+                       "varying vec2 vTextureCoord;",
+                       "varying vec3 vLightWeight;",
+                       "varying vec4 vFogWeight;",
+
+                       "void main(void) {",
+                       "       vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",
+                       "       gl_Position = uPerspectiveMatrix * v_Position;",
+                       "       vTextureCoord = aTextureCoord;",
+                       "       float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",
+                       "       vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.6, 1.0);",
+                       "       vec3 transNormalVector = nNormalMatrix * aVertexNormal;",
+
+                       "       float vLightWeightFirst = 0.0;",
+                       "       float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",
+
+                       "       vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",
+                       "}"
+               ].join( "\n" ),
+               FRAGMENT_SHADER = [
+                       "precision mediump float;",
+                       "varying vec2 vTextureCoord;",
+                       "varying vec3 vLightWeight;",
+                       "uniform sampler2D uSampler;",
+                       "varying vec4 vFogWeight;",
+
+                       "void main(void) {",
+                       "       vec4 TextureColor;",
+                       "       if ( vTextureCoord.s <= 0.01 || vTextureCoord.s >= 0.99 || vTextureCoord.t <= 0.01 || vTextureCoord.t >= 0.99 ) {",
+                       "               TextureColor = vec4(1.0, 1.0, 1.0, 0.5);",
+                       "       } else {",
+                       "               TextureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));",
+                       "       }",
+                       "       TextureColor *= vFogWeight;",
+                       "       gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",
+                       "}"
+               ].join( "\n" );
+
+       function Node() {
+               this.vertices = [
+                       -1.0, -1.0, 0.0,
+                       1.0, -1.0, 0.0,
+                       1.0,  1.0, 0.0,
+                       -1.0,  1.0, 0.0
+               ];
+               this.textureCoords = [
+                       1.0, 0.0,
+                       0.0, 0.0,
+                       0.0, 1.0,
+                       1.0, 1.0
+               ];
+               this.normalVectors = [
+                       0.0, 0.0, 1.0,
+                       0.0, 0.0, 1.0,
+                       0.0, 0.0, 1.0,
+                       0.0, 0.0, 1.0
+               ];
+               this.texture = null;
+               this.textureBuffer = null;
+               this.textureBufferItemSize = 0;
+               this.mashOrder = [];
+               this.mvMatrix = null;
+               this.level = -1;
+               this.targetLevel = 0;
+               this.drawable = false;
+               this.image = null;
+               this.imageID = 0;
+       }
+
+       $.widget( "tizen.gallery3d", $.mobile.widget, {
+               options: {
+                       thumbnailCache: false
+               },
+
+               _MAX_ITEM_COUNT: 28,
+               _ANIMATION_END: 999,
+               _DURATION_DEFAULT: 300,
+               _DURATION_FIRST: 1600,
+               _VIEWPORT_WIDTH: 1024,
+               _VIEWPORT_HEIGHT: 456,
+               _DIRECTION_LEFT: -1,
+               _DIRECTION_RIGHT: +1,
+
+               _gl: null,
+               _shaderProgram : null,
+               _positionBuffer : null,
+               _textureCoordBuffer : null,
+               _normalVectorBuffer : null,
+               _nodes: null,
+               _pMatrix : null,
+               _animationID: 0,
+               _dragInterval : 0,
+               _startTime : 0,
+               _sumTime : 0,
+               _lightsPositionStack : [
+                       [0.0, 0.0, -1.0],       // back
+                       [-0.2, 0.0, 0.7]        // front
+               ],
+               _path: null,
+               _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),
+               _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),
+               _canvas: null,
+               _imageList: [],
+               _maxDrawLength: 0,
+               _firstImageNumber: 0,
+               _lastImageNumber: 0,
+
+               _create: function () {
+                       var self = this,
+                               view = self.element,
+                               option = self.options;
+
+                       self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );
+
+                       view.addClass( "ui-gallery3d" ).append( self._canvas );
+                       self._addBehavier();
+
+                       self._dragInterval = 1000 / 30; // 30fps
+
+                       $.each( self.options, function ( key, value ) {
+                               self.options[ key ] = undefined;
+                               self._setOption( key, value );
+                       });
+
+               },
+
+               _setOption: function ( key, value ) {
+                       switch ( key ) {
+                       case "thumbnailCache" :
+                               if ( typeof value === "string" ) {
+                                       value = ( value === "true" ) ? true : false;
+                               } else {
+                                       value = !!value;
+                               }
+                               this._reset();
+                               break;
+                       }
+
+                       $.mobile.widget.prototype._setOption.call( this, key, value );
+               },
+
+               _init: function ( canvas ) {
+                       var self = this,
+                               pathPoints = [
+                                       [40, 0, -48],
+                                       [-12, 0, -40],  // contorl Point of Point1
+                                       [24, 0, -9],            // contorl Point of Point2
+                                       [-5, 0, -5]
+                               ],
+                               i;
+
+                       canvas = canvas || self._canvas;
+
+                       if ( !canvas ) {
+                               return;
+                       }
+
+                       self._gl = self._gl || self._initGL( canvas[0] );
+                       if ( !self._gl ) {
+                               return;
+                       }
+
+                       if ( !self._imageList ) {
+                               return;
+                       }
+
+                       self._shaderProgram = self._shaderProgram || self._initShader( self._gl );
+                       if ( !self._shaderProgram ) {
+                               return;
+                       }
+
+                       if ( self._imageList.length > self._MAX_ITEM_COUNT ) {
+                               self._firstImageNumber = self._imageList.length - 1;
+                               self._lastImageNumber = self._MAX_ITEM_COUNT - 1;
+                       }
+
+                       self._nodes = self._initBuffers( self._gl, self._shaderProgram );
+
+                       self._initTextures( self._gl, self._nodes );
+
+                       self._path = $.motionpath( "bezier2d", {
+                               points: pathPoints,
+                               maxLevel: self._MAX_ITEM_COUNT
+                       } );
+                       for ( i = 0; i < self._nodes.length; i += 1 ) {
+                               self._path.levels[i] = self._path.levels[i + 1] || 0;
+                               self._nodes[i].level = i;
+                       }
+               },
+
+               _final: function ( canvas ) {
+                       var self = this,
+                               gl = self._gl;
+
+                       if ( !gl ) {
+                               return;
+                       }
+
+                       canvas = canvas || self._canvas;
+
+                       $( self._nodes ).each( function ( i ) {
+                               var node = self._nodes[i];
+                               gl.deleteTexture( node.texture );
+                               node.texture = null;
+                       });
+                       self._nodes = null;
+
+                       gl.deleteBuffer( self._positionBuffer );
+                       self._positionBuffer = null;
+                       gl.deleteBuffer( self._textureCoordBuffer );
+                       self._textureCoordBuffer = null;
+                       gl.deleteBuffer( self._normalVectorBuffer );
+                       self._normalVectorBuffer = null;
+
+                       $.webgl.shader.deleteShaders( gl );
+                       gl.deleteProgram( self._shaderProgram );
+                       self._shaderProgram = null;
+
+                       self._gl = gl = null;
+               },
+
+               _addBehavier : function () {
+                       var self = this,
+                               view = self.element,
+                               canvas = self._canvas,
+                               touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),
+                               touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",
+                               touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",
+                               touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";
+
+                       $( document ).unbind( ".gallery3d" ).bind( "pagechange.gallery3d", function ( e ) {
+                               $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
+                       }).bind( "pageremove.gallery3d", function ( e ) {
+                               $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );
+                       });
+
+                       $( window ).unbind( ".gallery3d" ).bind( "resize.gallery3d orientationchange.gallery3d", function ( e ) {
+                               $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
+                       }).bind( "unload.gallery3d", function ( e ) {
+                               $( e.target ).find( ".ui-gallery3d" ).trigger( "_destory" );
+                       });
+
+                       view.bind( "_destory", function ( e ) {
+                               self._final();
+                       });
+
+                       canvas.bind( "webglcontextlost", function ( e ) {
+                               e.preventDefault();
+                       }).bind( "webglcontextrestored", function ( e ) {
+                               self._init();
+                       }).bind( touchStartEvt, function ( e ) {
+                               var i = 0,
+                                       startX = 0,
+                                       deltaMaxSteps = 20,
+                                       deltas = [ deltaMaxSteps ],
+                                       deltaTimes = [ deltaMaxSteps ],
+                                       deltaIndex = 0,
+                                       dragValue = 0,
+                                       dragDirection = false,
+                                       prevTime = 0;
+
+                               e.preventDefault();
+                               e.stopPropagation();
+
+                               if ( self._imageList.length <= 1 ) {
+                                       return;
+                               }
+
+                               self._stop();
+
+                               startX =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
+                               prevTime = $.now();
+
+                               for ( i = 0; i < deltaMaxSteps; i += 1 ) {
+                                       deltas[i] = startX;
+                                       deltaTimes[i] = $.now();
+                               }
+
+                               deltaIndex += 1;
+
+                               view.bind( touchMoveEvt, function ( e ) {
+                                       var x, dx, interval;
+
+                                       e.preventDefault();
+                                       e.stopPropagation();
+
+                                       x =  $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
+                                       dx = startX - x;
+
+                                       deltas[deltaIndex] = x;
+                                       deltaTimes[deltaIndex] = $.now();
+                                       interval = deltaTimes[deltaIndex] - prevTime;
+
+                                       deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;
+
+                                       // Validation of drag
+                                       if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {
+                                               if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {
+                                                       dragValue = 0;
+                                                       dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;
+                                               }
+
+                                               dragValue += Math.abs( dx ) / 100;
+                                               if ( dragValue >= 1 ) {
+                                                       self._setPosition( self._ANIMATION_END, dragDirection );
+                                                       dragValue = 0;
+                                               } else {
+                                                       self._setPosition( dragValue, dragDirection );
+                                               }
+                                               self._drawScene();
+                                               startX = x;
+                                               prevTime = $.now();
+                                       }
+                               }).bind( touchEndEvt, function ( e ) {
+                                       var baseTime = 0,
+                                               recent = -1,
+                                               index = 0,
+                                               previous = 0,
+                                               baseTimeRatio = 0,
+                                               fx = 0,
+                                               lastX = 0,
+                                               velocityX = 0,
+                                               dx = 0,
+                                               isSwipe = true,
+                                               direction;
+
+                                       e.preventDefault();
+                                       e.stopPropagation();
+
+                                       // Validation of swipe
+                                       baseTime = $.now() - self._swipeThresholdOfBasetimeGap;
+                                       lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
+                                       dx = startX - lastX;
+                                       startX = 0;
+                                       for ( i = 0; i < deltaMaxSteps; i += 1 ) {
+                                               index = ( deltaIndex + i ) % deltaMaxSteps;
+                                               if ( deltaTimes[index] > baseTime ) {
+                                                       recent = index;
+                                                       break;
+                                               }
+                                       }
+                                       if ( recent < 0 ) {
+                                               isSwipe = false;
+                                       }
+
+                                       if ( isSwipe ) {
+                                               previous = recent;
+                                               for ( i = 0; i < deltaMaxSteps; i += 1 ) {
+                                                       previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;
+                                                       if ( deltaTimes[previous] < deltaTimes[recent] ) {
+                                                               break;
+                                                       }
+                                               }
+                                               // too slow or too fast
+                                               if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {
+                                                       isSwipe = false;
+                                               }
+                                       }
+
+                                       if ( isSwipe ) {
+                                               baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );
+                                               fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];
+                                               if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {
+                                                       fx = lastX;
+                                               }
+                                               velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );
+                                       }
+
+                                       if ( isSwipe && velocityX ) {
+                                               direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;
+                                               self._run( direction, Math.abs( velocityX ), dragValue );
+                                       } else if ( dragDirection !== 0 && dragValue ) {
+                                               self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );
+                                       }
+
+                                       view.unbind( ".gallery3d" );
+                               }).bind( touchLeaveEvt, function ( e ) {
+                                       view.trigger( touchEndEvt );
+                               });
+                       });
+               },
+
+               // ----------------------------------------------------------
+               // WebGL
+               // ----------------------------------------------------------
+               _initGL: function ( canvas ) {
+                       var self = this,
+                               gl;
+
+                       gl = getContext3D( canvas );
+                       if ( !gl ) {
+                               window.alert( "There's no WebGL context available!!!" );
+                               return null;
+                       }
+
+                       gl.enable( gl.BLEND );
+                       gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
+
+                       gl.enable( gl.DEPTH_TEST );
+                       gl.depthFunc( gl.LEQUAL );
+
+                       canvas.width = self._VIEWPORT_WIDTH;
+                       canvas.height = self._VIEWPORT_HEIGHT;
+                       gl.viewportWidth = canvas.width;
+                       gl.viewportHeight = canvas.height;
+                       gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );
+                       self._pMatrix = mat4.create();
+                       mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );
+
+                       gl.clearColor( 0.15, 0.15, 0.15, 1.0 );
+                       gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
+
+                       return gl;
+               },
+
+               _initShader : function ( gl ) {
+                       var self = this,
+                               shaderProgram;
+
+                       shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );
+                       gl.useProgram( shaderProgram );
+
+                       shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );
+                       gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );
+
+                       shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );
+                       gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );
+
+                       // Set light normal vectors for lighting~
+                       shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );
+                       gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );
+
+                       shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");
+                       shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");
+                       shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");
+
+                       // Set light variables~
+                       shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");
+                       shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");
+                       shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");
+                       shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");
+                       shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");
+
+                       return shaderProgram;
+               },
+
+               _initBuffers: function ( gl, shaderProgram ) {
+                       var self = this,
+                               i = 0,
+                               mashBase = 0,
+                               vertices = [],
+                               textureCoords = [],
+                               normalVectors = [],
+                               nodes = [],
+                               maxDrawLength = self._MAX_ITEM_COUNT;
+
+                       for ( i = 0; i < self._imageList.length + 1; i += 1 ) {
+                               nodes[i] = new Node();
+                               $.merge( vertices, nodes[i].vertices );
+                               $.merge( textureCoords, nodes[i].textureCoords );
+                               $.merge( normalVectors, nodes[i].normalVectors );
+
+                               nodes[i].textureBuffer = gl.createBuffer();
+                               gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );
+                               mashBase = i * 4;
+                               nodes[i].meshOrder = [
+                                       mashBase, mashBase + 1, mashBase + 2,
+                                       mashBase + 2, mashBase + 3, mashBase
+                               ];
+                               gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );
+                               gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory
+                               nodes[i].textureBufferItemSize = 6;
+                       }
+
+                       self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );
+                       self._positionBuffer.itemSize = 3;
+
+                       self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );
+                       self._textureCoordBuffer.itemSize = 2;
+
+                       self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light
+                       self._normalVectorBuffer.itemSize = 3;
+
+                       // Ambient light
+                       gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );
+                       // Direcntion light
+                       gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );
+
+                       return nodes;
+               },
+
+               // ----------------------------------------------------------
+               // Texture
+               // ----------------------------------------------------------
+               _initTextures: function ( gl, nodes ) {
+                       var self = this;
+
+                       $( nodes ).each( function ( i ) {
+                               var node = nodes[i],
+                                       url;
+
+                               if ( !self._imageList[i] ) {
+                                       return false;
+                               }
+
+                               url = self._imageList[i].src;
+                               node.texture = gl.createTexture();
+                               self._loadImage( url, i, i, gl, nodes );
+                       });
+               },
+
+               _loadImage: function ( url, i, imageID, gl, nodes ) {
+                       var self = this,
+                               isMipmap = false,
+                               image,
+                               node;
+
+                       gl = gl || self._gl;
+                       nodes = nodes || self._nodes;
+                       isMipmap = isMipmap || false;
+                       node = nodes[i];
+                       node.image = node.image || new Image();
+
+                       $( node.image ).one( "load", function ( e ) {
+                               self._bindTexture( gl, node, this, isMipmap );
+                               node.imageID = imageID;
+
+                               if ( !self._animationID ) {
+                                       self._setPosition( 0, 0 );
+                               }
+                       });
+
+                       if ( self.options.thumbnailCache ) {
+                               $.imageloader.getThumbnail( url, function ( result ) {
+                                       if ( result === "NOT_FOUND_ERR" ) {
+                                               $.imageloader.setThumbnail( url, function ( result ) {
+                                                       if ( result && result.length > 30 ) {
+                                                               node.image.src = result;
+                                                               isMipmap = true;
+                                                       } else {
+                                                               node.image.src = url;
+                                                       }
+                                               });
+                                       } else if ( result && result.length > 30 ) {
+                                               node.image.src = result;
+                                               isMipmap = true;
+                                       } else {
+                                               node.image.src = url;
+                                       }
+                               });
+                       } else {
+                               node.image.src = url;
+                       }
+               },
+
+               _bindTexture: function ( gl, node, image, isMipmap ) {
+                       if ( !node || !node.texture ) {
+                               return;
+                       }
+
+                       gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
+
+                       gl.bindTexture( gl.TEXTURE_2D, node.texture );
+                       gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
+
+                       if ( isMipmap ) {
+                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
+                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );
+                               gl.generateMipmap( gl.TEXTURE_2D );
+                       } else {
+                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
+                               gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
+                       }
+
+                       gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
+                       gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
+
+                       node.texture.loaded = true;
+
+                       // release texture memory
+                       gl.bindTexture( gl.TEXTURE_2D, null );
+               },
+
+               // ----------------------------------------------------------
+               // rendering
+               // ----------------------------------------------------------
+               _setPosition: function ( progress, direction ) {
+                       var self = this,
+                               nodes = self._nodes,
+                               imageList = self._imageList,
+                               imageListLength = imageList.length,
+                               itemCount = self._MAX_ITEM_COUNT,
+                               displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,
+                               nextLevelLenth = 0,
+                               i = 0,
+                               t = 0,
+                               position = 0,
+                               angle = 0,
+                               current = 0,
+                               next = 0,
+                               nextLevel = 0,
+                               path = self._path,
+                               nextImageID = 0;
+
+                       nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;
+
+                       if ( !nodes[i].level ) {
+                               nodes[i].level = displayLength;
+                       }
+
+                       for ( i = 0; i < displayLength; i += 1 ) {
+                               if ( !nodes[i].mvMatrix ) {
+                                       nodes[i].mvMatrix = mat4.create();
+                               }
+
+                               if ( direction > 0 && nodes[i].level >= displayLength ) {
+                                       nodes[i].level = 0;
+                               }
+
+                               current = path.levels[nodes[i].level];
+                               nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;
+                               next = path.levels[nextLevel];
+
+                               if ( imageListLength > itemCount ) {
+                                       if ( direction > 0 && nextLevel === 1
+                                                       && self._firstImageNumber !== nodes[i].imageID ) {
+                                               self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );
+                                       } else if ( direction < 0 && nextLevel === nextLevelLenth - 1
+                                                       && self._lastImageNumber !== nodes[i].imageID ) {
+                                               self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );
+                                       }
+                               }
+
+                               mat4.identity( nodes[i].mvMatrix );
+                               mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );
+                               mat4.rotate( nodes[i].mvMatrix, self._degreeToRadian( 19 ), [1, 0, 0] );
+
+                               t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );
+
+                               if ( progress >= self._ANIMATION_END ) {
+                                       nodes[i].level = nextLevel || displayLength;
+                                       t = path.levels[nodes[i].level];
+                               }
+
+                               if ( ( progress < self._ANIMATION_END )
+                                               && ( direction <= 0 && nodes[i].level < 1 ) ) {
+                                       nodes[i].drawable = false;
+                               } else {
+                                       nodes[i].drawable = true;
+                               }
+
+                               if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {
+                                       self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );
+                               }
+
+                               position = path.getPosition( t );
+                               angle = path.getAngle( t );
+
+                               mat4.translate( nodes[i].mvMatrix, position );
+                               mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );
+                       }
+
+                       if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {
+                               self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;
+                               if ( self._firstImageNumber < 0 ) {
+                                       self._firstImageNumber = imageListLength - 1;
+                               }
+
+                               self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;
+                               if ( self._lastImageNumber < 0 ) {
+                                       self._lastImageNumber = imageListLength - 1;
+                               }
+                       }
+                       self._drawScene();
+               },
+
+               _drawScene: function () {
+                       if ( !this._gl || !this._shaderProgram ) {
+                               return;
+                       }
+
+                       var self = this,
+                               gl = self._gl,
+                               shaderProgram = self._shaderProgram,
+                               nodes = self._nodes,
+                               nodesLength = nodes.length,
+                               i;
+
+                       gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
+
+                       gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );
+                       gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
+
+                       gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );
+                       gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );
+
+                       gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );
+                       gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );
+
+                       for ( i = 0; i < nodesLength; i += 1 ) {
+                               if ( nodes[i].drawable ) {
+                                       self._drawElement( self._pMatrix, nodes[i] );
+                               }
+                       }
+               },
+
+               _drawElement: function ( perspectiveMatrix, targetNode ) {
+                       var self = this,
+                               gl = self._gl,
+                               shaderProgram = self._shaderProgram,
+                               moveMatrix = targetNode.mvMatrix,
+                               texture = targetNode.texture,
+                               meshIndexBuffer = targetNode.textureBuffer,
+                               meshIndexBufferItemSize = targetNode.textureBufferItemSize,
+                               lightPositions = self._lightsPositionStack,
+                               LightDir,
+                               normalMatrix;
+
+                       if ( !moveMatrix ) {
+                               return;
+                       }
+
+                       gl.activeTexture( gl.TEXTURE0 );
+                       if ( texture && texture.loaded ) {
+                               gl.bindTexture( gl.TEXTURE_2D, texture );
+                       }
+                       gl.uniform1i( shaderProgram.sampleUniform, 0 );
+
+                       LightDir = vec3.create();
+                       vec3.normalize( lightPositions[0], LightDir );
+                       vec3.scale( LightDir, -8 );
+                       gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );
+
+                       vec3.normalize( lightPositions[1], LightDir );
+                       vec3.scale( LightDir, -1 );
+                       gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );
+                       gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );
+
+                       gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );
+                       gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );
+
+                       normalMatrix = mat3.create();
+                       mat4.toInverseMat3( moveMatrix, normalMatrix );
+                       mat3.transpose( normalMatrix );
+                       gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );
+
+                       gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );
+
+                       // release buffer memory
+                       gl.bindBuffer( gl.ARRAY_BUFFER, null );
+                       gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
+
+                       // release texture memory
+                       gl.bindTexture( gl.TEXTURE_2D, null );
+               },
+
+               // ----------------------------------------------------------
+               // Animation
+               // ----------------------------------------------------------
+               _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {
+                       var self = this,
+                               timeNow = $.now(),
+                               progress,
+                               removeCount = 0;
+
+                       easingType = easingType || "linear";
+                       startValue = startValue || 0;
+                       _removeCount = _removeCount || 0;
+
+                       if ( self._sumTime >= duration ) {
+                               self._setPosition( self._ANIMATION_END, direction );
+                               self._stop();
+                               return;
+                       }
+
+                       if ( self._startTime === 0 ) {
+                               self._startTime = timeNow;
+                       } else {
+                               self._sumTime = timeNow - self._startTime;
+                               progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );
+                               removeCount = parseInt( Math.abs( progress ), 10 );
+
+                               if ( _removeCount !== removeCount ) {
+                                       self._setPosition( self._ANIMATION_END, direction );
+                                       _removeCount = removeCount;
+
+                                       if ( ( repeatCount - _removeCount ) >= 0 ) {
+                                               self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
+                                       } else {
+                                               self._stop();
+                                       }
+                                       return;
+                               }
+
+                               self._setPosition( progress - _removeCount, direction );
+                       }
+
+                       self._animationID = window.requestAnimationFrame( function () {
+                               self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
+                       });
+               },
+
+               _run: function ( direction, repeatCount, startValue ) {
+                       var self = this,
+                               repeat = repeatCount || 0,
+                               duration = self._DURATION_DEFAULT * ( repeat + 1 );
+
+                       if ( self._imageList.length <= 1 ) {
+                               return;
+                       }
+
+                       startValue = startValue || 0;
+                       duration = ( duration >= 0 ) ? duration : 0;
+
+                       if ( self._animationID ) {
+                               self._setPosition( self._ANIMATION_END, direction );
+                               self._stop();
+                       }
+
+                       self._animate( "easeOutExpo", duration, direction, repeat, startValue );
+               },
+
+               _reset: function () {
+                       if ( !this._canvas || !this._gl ) {
+                               return;
+                       }
+
+                       this._final();
+                       this._init();
+                       this.refresh();
+               },
+
+               _stop: function () {
+                       if ( this._animationID ) {
+                               window.cancelAnimationFrame( this._animationID );
+                       }
+                       this._animationID = 0;
+
+                       this._startTime = 0;
+                       this._sumTime = 0;
+               },
+
+               _degreeToRadian: function ( degree ) {
+                       return degree * Math.PI / 180;
+               },
+
+               next: function () {
+                       this._run( this._DIRECTION_LEFT , 0 );
+               },
+
+               prev: function () {
+                       this._run( this._DIRECTION_RIGHT, 0 );
+               },
+
+               refresh: function () {
+                       var view = this.element,
+                               canvas = view.find( "canvas.ui-gallery3d-canvas" );
+
+                       if ( canvas.width() !== view.width() ) {
+                               canvas.width( view.width() );
+                       }
+
+                       if ( !this._animationID ) {
+                               this._setPosition( 0, 0 );
+                       }
+               },
+
+               select: function ( index ) {
+                       var nodes = this._nodes,
+                               repeat,
+                               i,
+                               imageID,
+                               object = null,
+                               target = 0,
+                               direction = 0;
+
+                       if ( index && this._animationID ) {
+                               this._stop();
+                       }
+
+                       for ( i in nodes ) {
+                               if ( nodes[i].level === 1 ) {
+                                       object = this._imageList[ nodes[i].imageID ];
+                                       imageID = nodes[i].imageID;
+                                       break;
+                               }
+                       }
+
+                       if ( !index ) {
+                               return object;
+                       }
+
+                       if ( index < 0 && index >= this._imageList.length ) {
+                               return;
+                       }
+
+                       target = index - imageID;
+                       direction = ( target > 0 ) ? this._DIRECTION_LEFT
+                               : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );
+                       if ( direction ) {
+                               this._run( direction, Math.abs( target ) - 1  );
+                       }
+               },
+
+               add: function ( item, index ) {
+                       if ( !item ) {
+                               return;
+                       }
+
+                       if ( typeof item === "string" ) {
+                               item = { "src" : item };
+                       }
+
+                       index = index || 0;
+                       if ( typeof index !== "number" && index < 0
+                                       && index >= this._imageList.length ) {
+                               return;
+                       }
+
+                       this._imageList.splice( index, 0, item );
+                       if ( this._gl ) {
+                               this._reset();
+                       }
+               },
+
+               remove: function ( index ) {
+                       index = index || 0;
+                       if ( typeof index !== "number" && index < 0
+                                       && index >= this._imageList.length ) {
+                               return;
+                       }
+
+                       this._imageList.splice( index, 1 );
+                       if ( this._gl ) {
+                               this._reset();
+                       }
+               },
+
+               clearThumbnailCache: function () {
+                       if ( !this._nodes || ( this._nodes.length <= 0 ) ) {
+                               return;
+                       }
+
+                       var i, url;
+                       for ( i = 0; i < this._imageList.length; i += 1 ) {
+                               url = this._imageList[i].src;
+                               $.imageloader.removeThumbnail( url );
+                       }
+               },
+
+               empty: function () {
+                       this._imageList = [];
+                       this._reset();
+               },
+
+               length: function () {
+                       return this._imageList.length;
+               }
+       });
+
+       $( document ).bind( "pagecreate create", function ( e ) {
+               $( ":jqmData(role='gallery3d')" ).gallery3d();
+       });
+
+} ( jQuery, document, window ) );
+
+//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
+} );
 //>>excludeEnd("jqmBuildExclude");
\ No newline at end of file