1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: 3D photo gallery widget.
4 //>>group: Tizen:Widgets
6 define( [ "components/imageloader", "components/motionpath", "components/webgl" ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
10 /* ***************************************************************************
13 Version 1.1, April, 2013
15 http://floralicense.org/license/
17 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
21 "License" shall mean the terms and conditions for use, reproduction,
22 and distribution as defined by Sections 1 through 9 of this document.
24 "Licensor" shall mean the copyright owner or entity authorized by
25 the copyright owner that is granting the License.
27 "Legal Entity" shall mean the union of the acting entity and
28 all other entities that control, are controlled by, or are
29 under common control with that entity. For the purposes of
30 this definition, "control" means (i) the power, direct or indirect,
31 to cause the direction or management of such entity,
32 whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
33 or more of the outstanding shares, or (iii) beneficial ownership of
36 "You" (or "Your") shall mean an individual or Legal Entity
37 exercising permissions granted by this License.
39 "Source" form shall mean the preferred form for making modifications,
40 including but not limited to software source code, documentation source,
41 and configuration files.
43 "Object" form shall mean any form resulting from mechanical
44 transformation or translation of a Source form, including but
45 not limited to compiled object code, generated documentation,
46 and conversions to other media types.
48 "Work" shall mean the work of authorship, whether in Source or Object form,
49 made available under the License, as indicated by a copyright notice
50 that is included in or attached to the work (an example is provided
51 in the Appendix below).
53 "Derivative Works" shall mean any work, whether in Source or Object form,
54 that is based on (or derived from) the Work and for which the editorial
55 revisions, annotations, elaborations, or other modifications represent,
56 as a whole, an original work of authorship. For the purposes of this License,
57 Derivative Works shall not include works that remain separable from,
58 or merely link (or bind by name) to the interfaces of, the Work and
59 Derivative Works thereof.
61 "Contribution" shall mean any work of authorship, including the original
62 version of the Work and any modifications or additions to that Work or
63 Derivative Works thereof, that is intentionally submitted to Licensor
64 for inclusion in the Work by the copyright owner or by an individual or
65 Legal Entity authorized to submit on behalf of the copyright owner.
66 For the purposes of this definition, "submitted" means any form of
67 electronic, verbal, or written communication sent to the Licensor or
68 its representatives, including but not limited to communication on
69 electronic mailing lists, source code control systems, and issue
70 tracking systems that are managed by, or on behalf of, the Licensor
71 for the purpose of discussing and improving the Work, but excluding
72 communication that is conspicuously marked or otherwise designated
73 in writing by the copyright owner as "Not a Contribution."
75 "Contributor" shall mean Licensor and any individual or Legal Entity
76 on behalf of whom a Contribution has been received by Licensor and
77 subsequently incorporated within the Work.
79 "Tizen Certified Platform" shall mean a software platform that complies
80 with the standards set forth in the Tizen Compliance Specification
81 and passes the Tizen Compliance Tests as defined from time to time
82 by the Tizen Technical Steering Group and certified by the Tizen
83 Association or its designated agent.
85 2. Grant of Copyright License. Subject to the terms and conditions of
86 this License, each Contributor hereby grants to You a perpetual,
87 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
88 copyright license to reproduce, prepare Derivative Works of,
89 publicly display, publicly perform, sublicense, and distribute the
90 Work and such Derivative Works in Source or Object form.
92 3. Grant of Patent License. Subject to the terms and conditions of
93 this License, each Contributor hereby grants to You a perpetual,
94 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
95 (except as stated in this section) patent license to make, have made,
96 use, offer to sell, sell, import, and otherwise transfer the Work
97 solely as incorporated into a Tizen Certified Platform, where such
98 license applies only to those patent claims licensable by such
99 Contributor that are necessarily infringed by their Contribution(s)
100 alone or by combination of their Contribution(s) with the Work solely
101 as incorporated into a Tizen Certified Platform to which such
102 Contribution(s) was submitted. If You institute patent litigation
103 against any entity (including a cross-claim or counterclaim
104 in a lawsuit) alleging that the Work or a Contribution incorporated
105 within the Work constitutes direct or contributory patent infringement,
106 then any patent licenses granted to You under this License for that
107 Work shall terminate as of the date such litigation is filed.
109 4. Redistribution. You may reproduce and distribute copies of the
110 Work or Derivative Works thereof pursuant to the copyright license
111 above, in any medium, with or without modifications, and in Source or
112 Object form, provided that You meet the following conditions:
114 1. You must give any other recipients of the Work or Derivative Works
115 a copy of this License; and
116 2. You must cause any modified files to carry prominent notices stating
117 that You changed the files; and
118 3. You must retain, in the Source form of any Derivative Works that
119 You distribute, all copyright, patent, trademark, and attribution
120 notices from the Source form of the Work, excluding those notices
121 that do not pertain to any part of the Derivative Works; and
122 4. If the Work includes a "NOTICE" text file as part of its distribution,
123 then any Derivative Works that You distribute must include a readable
124 copy of the attribution notices contained within such NOTICE file,
125 excluding those notices that do not pertain to any part of
126 the Derivative Works, in at least one of the following places:
127 within a NOTICE text file distributed as part of the Derivative Works;
128 within the Source form or documentation, if provided along with the
129 Derivative Works; or, within a display generated by the Derivative Works,
130 if and wherever such third-party notices normally appear.
131 The contents of the NOTICE file are for informational purposes only
132 and do not modify the License.
134 You may add Your own attribution notices within Derivative Works
135 that You distribute, alongside or as an addendum to the NOTICE text
136 from the Work, provided that such additional attribution notices
137 cannot be construed as modifying the License. You may add Your own
138 copyright statement to Your modifications and may provide additional or
139 different license terms and conditions for use, reproduction, or
140 distribution of Your modifications, or for any such Derivative Works
141 as a whole, provided Your use, reproduction, and distribution of
142 the Work otherwise complies with the conditions stated in this License.
144 5. Submission of Contributions. Unless You explicitly state otherwise,
145 any Contribution intentionally submitted for inclusion in the Work
146 by You to the Licensor shall be under the terms and conditions of
147 this License, without any additional terms or conditions.
148 Notwithstanding the above, nothing herein shall supersede or modify
149 the terms of any separate license agreement you may have executed
150 with Licensor regarding such Contributions.
152 6. Trademarks. This License does not grant permission to use the trade
153 names, trademarks, service marks, or product names of the Licensor,
154 except as required for reasonable and customary use in describing the
155 origin of the Work and reproducing the content of the NOTICE file.
157 7. Disclaimer of Warranty. Unless required by applicable law or
158 agreed to in writing, Licensor provides the Work (and each
159 Contributor provides its Contributions) on an "AS IS" BASIS,
160 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
161 implied, including, without limitation, any warranties or conditions
162 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
163 PARTICULAR PURPOSE. You are solely responsible for determining the
164 appropriateness of using or redistributing the Work and assume any
165 risks associated with Your exercise of permissions under this License.
167 8. Limitation of Liability. In no event and under no legal theory,
168 whether in tort (including negligence), contract, or otherwise,
169 unless required by applicable law (such as deliberate and grossly
170 negligent acts) or agreed to in writing, shall any Contributor be
171 liable to You for damages, including any direct, indirect, special,
172 incidental, or consequential damages of any character arising as a
173 result of this License or out of the use or inability to use the
174 Work (including but not limited to damages for loss of goodwill,
175 work stoppage, computer failure or malfunction, or any and all
176 other commercial damages or losses), even if such Contributor
177 has been advised of the possibility of such damages.
179 9. Accepting Warranty or Additional Liability. While redistributing
180 the Work or Derivative Works thereof, You may choose to offer,
181 and charge a fee for, acceptance of support, warranty, indemnity,
182 or other liability obligations and/or rights consistent with this
183 License. However, in accepting such obligations, You may act only
184 on Your own behalf and on Your sole responsibility, not on behalf
185 of any other Contributor, and only if You agree to indemnify,
186 defend, and hold each Contributor harmless for any liability
187 incurred by, or claims asserted against, such Contributor by reason
188 of your accepting any such warranty or additional liability.
190 END OF TERMS AND CONDITIONS
192 APPENDIX: How to apply the Flora License to your work
194 To apply the Flora License to your work, attach the following
195 boilerplate notice, with the fields enclosed by brackets "[]"
196 replaced with your own identifying information. (Don't include
197 the brackets!) The text should be enclosed in the appropriate
198 comment syntax for the file format. We also recommend that a
199 file or class name and description of purpose be included on the
200 same "printed page" as the copyright notice for easier
201 identification within third-party archives.
203 Copyright [yyyy] [name of copyright owner]
205 Licensed under the Flora License, Version 1.1 (the "License");
206 you may not use this file except in compliance with the License.
207 You may obtain a copy of the License at
209 http://floralicense.org/license/
211 Unless required by applicable law or agreed to in writing, software
212 distributed under the License is distributed on an "AS IS" BASIS,
213 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
214 See the License for the specific language governing permissions and
215 limitations under the License.
218 * Authors: Hyunsook Park <hyunsook.park@samsung.com>
219 * Wonseop Kim <wonseop.kim@samsung.com>
223 * The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
224 * To improve performance, the size of image(s) displayed on the screen should be a square(under
225 * 128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image
226 * resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"
227 * option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
228 * So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve
229 * launching time. A browser or web runtime engine should support "Web Storage" feature to use that
234 * data-thumbnail-cache : Determines whether to cache and resize images.
239 * : This method moves each image forward one by one.
241 * : This method moves each image backward one by one.
242 * select ( [number] )
243 * : When the "select" method is called with an argument, the method selects the image of given index.
244 * If the method is called with no argument, it will return the Javascript object having "src"
245 * attribute having the selected image's URL.
246 * add ( object or string [, number] )
247 * This method adds an image to Gallery3D widget.
248 * If the second argument isn't inputted, the image is added at the 0th position.
249 * remove ( [number] )
250 * : This method deletes an image from Gallery3d widget.
251 * The argument defines the index of the image to be deleted.
252 * If an argument isn't inputted, it removes current image.
253 * clearThumbnailCache ( void )
254 * : This method clears the cache data of all images when thumbnailCache option is set as 'true'.
256 * : This method updates and redraws current widget.
258 * : This method removes all of images from Gallery3D widget.
260 * : This method gets the number of images.
264 * select : Triggered when an image is selected.
269 * $( "#gallery3d" ).on( "gallery3dcreate", function () {
270 * $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
273 * <div id="gallery3d" data-role="gallery3d"></div>
278 The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
279 <br/><br/>To add an gallery3d widget to the application, use the following code:
282 $( "#gallery3d" ).on( "gallery3dcreate", function () {
283 $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
286 <div id="gallery3d" data-role="gallery3d"></div>
289 @property {Boolean} data-thumbnail-cache
290 Determines whether to cache and resize images.
291 To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).
292 "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
293 So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.
294 A browser or web runtime engine must support "Web Storage" feature to use this option.
298 Triggered when an image is selected.
301 $( "#gallery3d" ).on( "gallery3dcreate", function () {
302 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
303 .gallery3d( "add", { src: "2.jpg" } )
304 .gallery3d( "add", { src: "3.jpg" } );
305 }).on( "select", function ( event, data, index ) {
306 // Handle the select event
307 var urlOfImage = data.src, indexOfImage = index;
310 <div id="gallery3d" data-role="gallery3d"></div>
314 This method moves each image forward one by one.
317 $( "#gallery3d" ).on( "gallery3dcreate", function () {
318 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
319 .gallery3d( "add", { src: "2.jpg" } )
320 .gallery3d( "add", { src: "3.jpg" } )
321 .gallery3d( "next" );
324 <div id="gallery3d" data-role="gallery3d"></div>
328 This method moves each image backward one by one.
331 $( "#gallery3d" ).on( "gallery3dcreate", function () {
332 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
333 .gallery3d( "add", { src: "2.jpg" } )
334 .gallery3d( "add", { src: "3.jpg" } )
335 .gallery3d( "prev" );
338 <div id="gallery3d" data-role="gallery3d"></div>
342 When the "select" method is called with an argument, the method selects the image of given index.
343 If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image's URL.
346 $( "#gallery3d" ).on( "gallery3dcreate", function () {
347 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
348 .gallery3d( "add", { src: "2.jpg" } )
349 .gallery3d( "add", { src: "3.jpg" } );
350 var selectedImage = $("#gallery3d"). gallery3d( "select" );
351 // selectedImage = { src: "3.jpg" };
354 <div id="gallery3d" data-role="gallery3d"></div>
358 This method adds an image to Gallery3D widget.
359 The first argument is a Javascript object having a "src" attribute or a string of image's path.
360 The second argument is an index of images.
361 If second argument isn't inputted, the image is added at the 0th position.
364 $( "#gallery3d" ).on( "gallery3dcreate", function () {
365 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
366 .gallery3d( "add", "2.jpg", 1 );
369 <div id="gallery3d" data-role="gallery3d"></div>
373 This method deletes an image from Gallery3d widget.
374 The argument defines the index of the image to be deleted.
375 If an argument isn't inputted, it removes current image.
378 $( "#gallery3d" ).on( "gallery3dcreate", function () {
379 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
380 .gallery3d( "add", { src: "2.jpg" } )
381 .gallery3d( "add", { src: "3.jpg" } );
383 $( "#gallery3d" ).gallery3d( "remove" );
384 $( "#gallery3d" ).gallery3d( "remove", 1 );
387 <div id="gallery3d" data-role="gallery3d"></div>
390 @method clearThumbnailCache
391 This method clears the cache data of all images when thumbnailCache option is set as 'true'
394 $( "#gallery3d" ).on( "gallery3dcreate", function () {
395 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
396 .gallery3d( "add", { src: "2.jpg" } )
397 .gallery3d( "add", { src: "3.jpg" } );
399 $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );
402 <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>
406 This method updates and redraws current widget.
409 $( "#gallery3d" ).on( "gallery3dcreate", function () {
410 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
411 .gallery3d( "add", { src: "2.jpg" } )
412 .gallery3d( "add", { src: "3.jpg" } );
414 $( "#gallery3d" ).gallery3d( "refresh" );
417 <div id="gallery3d" data-role="gallery3d"></div>
421 This method removes all of images from Gallery3D widget.
424 $( "#gallery3d" ).on( "gallery3dcreate", function () {
425 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
426 .gallery3d( "add", { src: "2.jpg" } )
427 .gallery3d( "add", { src: "3.jpg" } );
429 $( "#gallery3d" ).gallery3d( "empty" );
432 <div id="gallery3d" data-role="gallery3d"></div>
436 This method gets the number of images.
439 $( "#gallery3d" ).on( "gallery3dcreate", function () {
440 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
441 .gallery3d( "add", { src: "2.jpg" } )
442 .gallery3d( "add", { src: "3.jpg" } );
444 var imagesLength = $( "#gallery3d" ).gallery3d( "length" );
448 <div id="gallery3d" data-role="gallery3d"></div>
451 ( function ( $, document, window, undefined ) {
459 this.textureCoords = [
465 this.normalVectors = [
472 this.textureBuffer = null;
473 this.textureBufferItemSize = 0;
475 this.mvMatrix = null;
477 this.targetLevel = 0;
478 this.drawable = false;
483 var isPreInitailization = false,
489 preInitialize = function () {
490 if ( isPreInitailization ) {
494 window.initGlMatrix( glMatrix );
497 "attribute vec3 aVertexPosition;",
498 "attribute vec2 aTextureCoord;",
499 "attribute vec3 aVertexNormal;",
500 "uniform mat4 uMoveMatrix;",
501 "uniform mat4 uPerspectiveMatrix;",
502 "uniform mat3 nNormalMatrix;",
503 "uniform vec3 uAmbientColor;",
504 "uniform vec3 uLightDirection;",
505 "uniform vec3 uDirectionColor;",
506 "uniform vec3 uLightDirection_first;",
507 "uniform vec3 uLightDirection_second;",
508 "varying vec2 vTextureCoord;",
509 "varying vec3 vLightWeight;",
510 "varying vec4 vFogWeight;",
513 " vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",
514 " gl_Position = uPerspectiveMatrix * v_Position;",
515 " vTextureCoord = aTextureCoord;",
516 " float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",
517 " vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.6, 1.0);",
518 " vec3 transNormalVector = nNormalMatrix * aVertexNormal;",
520 " float vLightWeightFirst = 0.0;",
521 " float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",
523 " vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",
528 "precision mediump float;",
529 "varying vec2 vTextureCoord;",
530 "varying vec3 vLightWeight;",
531 "uniform sampler2D uSampler;",
532 "varying vec4 vFogWeight;",
535 " vec4 TextureColor;",
536 " if ( vTextureCoord.s <= 0.01 || vTextureCoord.s >= 0.99 || vTextureCoord.t <= 0.01 || vTextureCoord.t >= 0.99 ) {",
537 " TextureColor = vec4(1.0, 1.0, 1.0, 0.5);",
539 " TextureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));",
541 " TextureColor *= vFogWeight;",
542 " gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",
546 GlArray32 = ( typeof window.Float32Array !== "undefined" ?
547 window.Float32Array :
548 ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) );
550 GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array );
552 isPreInitailization = true;
554 degreeToRadian = function ( degree ) {
555 return degree * Math.PI / 180;
557 getContext3D = function ( canvas ) {
559 contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];
561 for ( i = 0; i < contextNames.length; i += 1 ) {
563 gl = canvas.getContext( contextNames[i] );
568 $( canvas ).html( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );
574 requestAnimationFrame = function ( callback ) {
575 var id = window.setTimeout( callback, 1000 / 60 );
578 cancelAnimationFrame = function ( id ) {
579 window.clearTimeout( id );
582 $.widget( "tizen.gallery3d", $.mobile.widget, {
584 thumbnailCache: false
589 _DURATION_DEFAULT: 300,
590 _DURATION_FIRST: 1600,
591 _VIEWPORT_WIDTH: 1024,
592 _VIEWPORT_HEIGHT: 456,
594 _DIRECTION_RIGHT: +1,
597 _shaderProgram : null,
598 _positionBuffer : null,
599 _textureCoordBuffer : null,
600 _normalVectorBuffer : null,
607 _lightsPositionStack : [
608 [0.0, 0.0, -1.0], // back
609 [-0.2, 0.0, 0.7] // front
612 _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),
613 _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),
617 _firstImageNumber: 0,
620 _create: function () {
623 option = self.options;
627 self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );
629 view.addClass( "ui-gallery3d" ).append( self._canvas );
632 self._dragInterval = 1000 / 30; // 30fps
634 $.each( self.options, function ( key, value ) {
635 self.options[ key ] = undefined;
636 self._setOption( key, value );
640 destroy: function () {
642 $.mobile.widget.prototype.destroy.call( this );
645 _setOption: function ( key, value ) {
647 case "thumbnailCache" :
648 if ( typeof value === "string" ) {
649 value = ( value === "true" ) ? true : false;
657 $.mobile.widget.prototype._setOption.call( this, key, value );
660 _init: function ( canvas ) {
664 [-12, 0, -40], // contorl Point of Point1
665 [24, 0, -9], // contorl Point of Point2
670 canvas = canvas || self._canvas;
676 self._gl = self._gl || self._initGL( canvas[0] );
681 if ( !self._imageList ) {
685 self._shaderProgram = self._shaderProgram || self._initShader( self._gl );
686 if ( !self._shaderProgram ) {
690 if ( self._imageList.length > self._MAX_ITEM_COUNT ) {
691 self._firstImageNumber = self._imageList.length - 1;
692 self._lastImageNumber = self._MAX_ITEM_COUNT - 1;
695 self._nodes = self._initBuffers( self._gl, self._shaderProgram );
697 self._initTextures( self._gl, self._nodes );
699 self._path = $.motionpath( "bezier2d", {
701 maxLevel: self._MAX_ITEM_COUNT
703 for ( i = 0; i < self._nodes.length; i += 1 ) {
704 self._path.levels[i] = self._path.levels[i + 1] || 0;
705 self._nodes[i].level = i;
709 _final: function ( canvas ) {
719 canvas = canvas || self._canvas;
721 $( self._nodes ).each( function ( i ) {
722 var node = self._nodes[i];
723 gl.deleteTexture( node.texture );
728 gl.deleteBuffer( self._positionBuffer );
729 self._positionBuffer = null;
730 gl.deleteBuffer( self._textureCoordBuffer );
731 self._textureCoordBuffer = null;
732 gl.deleteBuffer( self._normalVectorBuffer );
733 self._normalVectorBuffer = null;
735 $.webgl.shader.deleteShaders( gl );
736 gl.deleteProgram( self._shaderProgram );
737 self._shaderProgram = null;
739 self._gl = gl = null;
742 _addBehavier : function () {
745 canvas = self._canvas,
746 touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),
747 touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",
748 touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",
749 touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";
751 canvas.on( "webglcontextlost", function ( e ) {
753 }).on( "webglcontextrestored", function ( e ) {
755 }).on( touchStartEvt, function ( e ) {
759 deltas = [ deltaMaxSteps ],
760 deltaTimes = [ deltaMaxSteps ],
763 dragDirection = false,
769 if ( self._imageList.length <= 1 ) {
775 startX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
778 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
780 deltaTimes[i] = $.now();
785 view.on( touchMoveEvt, function ( e ) {
791 x = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
794 deltas[deltaIndex] = x;
795 deltaTimes[deltaIndex] = $.now();
796 interval = deltaTimes[deltaIndex] - prevTime;
798 deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;
800 // Validation of drag
801 if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {
802 if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {
804 dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;
807 dragValue += Math.abs( dx ) / 100;
808 if ( dragValue >= 1 ) {
809 self._setPosition( self._ANIMATION_END, dragDirection );
812 self._setPosition( dragValue, dragDirection );
818 }).on( touchEndEvt, function ( e ) {
834 // Validation of swipe
835 baseTime = $.now() - self._swipeThresholdOfBasetimeGap;
836 lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
839 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
840 index = ( deltaIndex + i ) % deltaMaxSteps;
841 if ( deltaTimes[index] > baseTime ) {
852 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
853 previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;
854 if ( deltaTimes[previous] < deltaTimes[recent] ) {
858 // too slow or too fast
859 if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {
865 baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );
866 fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];
867 if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {
870 velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );
873 if ( isSwipe && velocityX ) {
874 direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;
875 self._run( direction, Math.abs( velocityX ), dragValue );
876 } else if ( dragDirection !== 0 && dragValue ) {
877 self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );
880 view.unbind( ".gallery3d" );
881 }).on( touchLeaveEvt, function ( e ) {
882 view.trigger( touchEndEvt );
887 // ----------------------------------------------------------
889 // ----------------------------------------------------------
890 _initGL: function ( canvas ) {
892 mat4 = glMatrix.mat4,
895 gl = getContext3D( canvas );
900 gl.enable( gl.BLEND );
901 gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
903 gl.enable( gl.DEPTH_TEST );
904 gl.depthFunc( gl.LEQUAL );
906 canvas.width = self._VIEWPORT_WIDTH;
907 canvas.height = self._VIEWPORT_HEIGHT;
908 gl.viewportWidth = canvas.width;
909 gl.viewportHeight = canvas.height;
910 gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );
911 self._pMatrix = mat4.create();
912 mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );
914 gl.clearColor( 0.15, 0.15, 0.15, 1.0 );
915 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
920 _initShader : function ( gl ) {
924 shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );
925 gl.useProgram( shaderProgram );
927 shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );
928 gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );
930 shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );
931 gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );
933 // Set light normal vectors for lighting~
934 shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );
935 gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );
937 shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");
938 shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");
939 shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");
941 // Set light variables~
942 shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");
943 shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");
944 shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");
945 shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");
946 shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");
948 return shaderProgram;
951 _initBuffers: function ( gl, shaderProgram ) {
959 maxDrawLength = self._MAX_ITEM_COUNT;
961 for ( i = 0; i < self._imageList.length + 1; i += 1 ) {
962 nodes[i] = new Node();
963 $.merge( vertices, nodes[i].vertices );
964 $.merge( textureCoords, nodes[i].textureCoords );
965 $.merge( normalVectors, nodes[i].normalVectors );
967 nodes[i].textureBuffer = gl.createBuffer();
968 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );
970 nodes[i].meshOrder = [
971 mashBase, mashBase + 1, mashBase + 2,
972 mashBase + 2, mashBase + 3, mashBase
974 gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );
975 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory
976 nodes[i].textureBufferItemSize = 6;
979 self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );
980 self._positionBuffer.itemSize = 3;
982 self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );
983 self._textureCoordBuffer.itemSize = 2;
985 self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light
986 self._normalVectorBuffer.itemSize = 3;
989 gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );
991 gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );
996 // ----------------------------------------------------------
998 // ----------------------------------------------------------
999 _initTextures: function ( gl, nodes ) {
1002 $( nodes ).each( function ( i ) {
1003 var node = nodes[i],
1006 if ( !self._imageList[i] ) {
1010 url = self._imageList[i].src;
1011 node.texture = gl.createTexture();
1012 self._loadImage( url, i, i, gl, nodes );
1016 _loadImage: function ( url, i, imageID, gl, nodes ) {
1022 gl = gl || self._gl;
1023 nodes = nodes || self._nodes;
1024 isMipmap = isMipmap || false;
1026 node.image = node.image || new Image();
1028 $( node.image ).one( "load", function ( e ) {
1029 self._bindTexture( gl, node, this, isMipmap );
1030 node.imageID = imageID;
1032 if ( !self._animationID ) {
1033 self._setPosition( 0, 0 );
1037 if ( self.options.thumbnailCache ) {
1038 $.imageloader.getThumbnail( url, function ( result ) {
1039 if ( result === "NOT_FOUND_ERR" ) {
1040 $.imageloader.setThumbnail( url, function ( result ) {
1041 if ( result && result.length > 30 ) {
1042 node.image.src = result;
1045 node.image.src = url;
1048 } else if ( result && result.length > 30 ) {
1049 node.image.src = result;
1052 node.image.src = url;
1056 node.image.src = url;
1060 _bindTexture: function ( gl, node, image, isMipmap ) {
1061 if ( !node || !node.texture ) {
1065 gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
1067 gl.bindTexture( gl.TEXTURE_2D, node.texture );
1068 gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
1071 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1072 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );
1073 gl.generateMipmap( gl.TEXTURE_2D );
1075 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1076 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
1079 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
1080 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
1082 node.texture.loaded = true;
1084 // release texture memory
1085 gl.bindTexture( gl.TEXTURE_2D, null );
1088 // ----------------------------------------------------------
1090 // ----------------------------------------------------------
1091 _setPosition: function ( progress, direction ) {
1093 mat4 = glMatrix.mat4,
1094 nodes = self._nodes,
1095 imageList = self._imageList,
1096 imageListLength = imageList.length,
1097 itemCount = self._MAX_ITEM_COUNT,
1098 displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,
1110 nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;
1112 if ( !nodes[i].level ) {
1113 nodes[i].level = displayLength;
1116 for ( i = 0; i < displayLength; i += 1 ) {
1117 if ( !nodes[i].mvMatrix ) {
1118 nodes[i].mvMatrix = mat4.create();
1121 if ( direction > 0 && nodes[i].level >= displayLength ) {
1125 current = path.levels[nodes[i].level];
1126 nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;
1127 next = path.levels[nextLevel];
1129 if ( imageListLength > itemCount ) {
1130 if ( direction > 0 && nextLevel === 1
1131 && self._firstImageNumber !== nodes[i].imageID ) {
1132 self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );
1133 } else if ( direction < 0 && nextLevel === nextLevelLenth - 1
1134 && self._lastImageNumber !== nodes[i].imageID ) {
1135 self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );
1139 mat4.identity( nodes[i].mvMatrix );
1140 mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );
1141 mat4.rotate( nodes[i].mvMatrix, degreeToRadian( 19 ), [1, 0, 0] );
1143 t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );
1145 if ( progress >= self._ANIMATION_END ) {
1146 nodes[i].level = nextLevel || displayLength;
1147 t = path.levels[nodes[i].level];
1150 if ( ( progress < self._ANIMATION_END )
1151 && ( direction <= 0 && nodes[i].level < 1 ) ) {
1152 nodes[i].drawable = false;
1154 nodes[i].drawable = true;
1157 if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {
1158 self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );
1161 position = path.getPosition( t );
1162 angle = path.getAngle( t );
1164 mat4.translate( nodes[i].mvMatrix, position );
1165 mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );
1168 if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {
1169 self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;
1170 if ( self._firstImageNumber < 0 ) {
1171 self._firstImageNumber = imageListLength - 1;
1174 self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;
1175 if ( self._lastImageNumber < 0 ) {
1176 self._lastImageNumber = imageListLength - 1;
1182 _drawScene: function () {
1183 if ( !this._gl || !this._shaderProgram ) {
1189 shaderProgram = self._shaderProgram,
1190 nodes = self._nodes,
1191 nodesLength = nodes.length,
1194 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
1196 gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );
1197 gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1199 gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );
1200 gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1202 gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );
1203 gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1205 for ( i = 0; i < nodesLength; i += 1 ) {
1206 if ( nodes[i].drawable ) {
1207 self._drawElement( self._pMatrix, nodes[i] );
1212 _drawElement: function ( perspectiveMatrix, targetNode ) {
1215 vec3 = glMatrix.vec3,
1216 mat3 = glMatrix.mat3,
1217 mat4 = glMatrix.mat4,
1218 shaderProgram = self._shaderProgram,
1219 moveMatrix = targetNode.mvMatrix,
1220 texture = targetNode.texture,
1221 meshIndexBuffer = targetNode.textureBuffer,
1222 meshIndexBufferItemSize = targetNode.textureBufferItemSize,
1223 lightPositions = self._lightsPositionStack,
1227 if ( !moveMatrix ) {
1231 gl.activeTexture( gl.TEXTURE0 );
1232 if ( texture && texture.loaded ) {
1233 gl.bindTexture( gl.TEXTURE_2D, texture );
1235 gl.uniform1i( shaderProgram.sampleUniform, 0 );
1237 LightDir = vec3.create();
1238 vec3.normalize( lightPositions[0], LightDir );
1239 vec3.scale( LightDir, -8 );
1240 gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );
1242 vec3.normalize( lightPositions[1], LightDir );
1243 vec3.scale( LightDir, -1 );
1244 gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );
1245 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );
1247 gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );
1248 gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );
1250 normalMatrix = mat3.create();
1251 mat4.toInverseMat3( moveMatrix, normalMatrix );
1252 mat3.transpose( normalMatrix );
1253 gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );
1255 gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );
1257 // release buffer memory
1258 gl.bindBuffer( gl.ARRAY_BUFFER, null );
1259 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
1261 // release texture memory
1262 gl.bindTexture( gl.TEXTURE_2D, null );
1265 // ----------------------------------------------------------
1267 // ----------------------------------------------------------
1268 _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {
1274 easingType = easingType || "linear";
1275 startValue = startValue || 0;
1276 _removeCount = _removeCount || 0;
1278 if ( self._sumTime >= duration ) {
1279 self._setPosition( self._ANIMATION_END, direction );
1284 if ( self._startTime === 0 ) {
1285 self._startTime = timeNow;
1287 self._sumTime = timeNow - self._startTime;
1288 progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );
1289 removeCount = parseInt( Math.abs( progress ), 10 );
1291 if ( _removeCount !== removeCount ) {
1292 self._setPosition( self._ANIMATION_END, direction );
1293 _removeCount = removeCount;
1295 if ( ( repeatCount - _removeCount ) >= 0 ) {
1296 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1303 self._setPosition( progress - _removeCount, direction );
1306 self._animationID = requestAnimationFrame( function () {
1307 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1311 _run: function ( direction, repeatCount, startValue ) {
1313 repeat = repeatCount || 0,
1314 duration = self._DURATION_DEFAULT * ( repeat + 1 );
1316 if ( self._imageList.length <= 1 ) {
1320 startValue = startValue || 0;
1321 duration = ( duration >= 0 ) ? duration : 0;
1323 if ( self._animationID ) {
1324 self._setPosition( self._ANIMATION_END, direction );
1328 self._animate( "easeOutExpo", duration, direction, repeat, startValue );
1331 _reset: function () {
1332 if ( !this._canvas || !this._gl ) {
1341 _stop: function () {
1342 if ( this._animationID ) {
1343 cancelAnimationFrame( this._animationID );
1345 this._animationID = 0;
1347 this._startTime = 0;
1352 this._run( this._DIRECTION_LEFT , 0 );
1356 this._run( this._DIRECTION_RIGHT, 0 );
1359 refresh: function () {
1360 var view = this.element,
1361 canvas = view.find( "canvas.ui-gallery3d-canvas" );
1363 if ( canvas.width() !== view.width() ) {
1364 canvas.width( view.width() );
1367 if ( !this._animationID ) {
1368 this._setPosition( 0, 0 );
1372 select: function ( index ) {
1373 var nodes = this._nodes,
1381 if ( index && this._animationID ) {
1385 for ( i in nodes ) {
1386 if ( nodes[i].level === 1 ) {
1387 object = this._imageList[ nodes[i].imageID ];
1388 imageID = nodes[i].imageID;
1397 if ( index < 0 && index >= this._imageList.length ) {
1401 target = index - imageID;
1402 direction = ( target > 0 ) ? this._DIRECTION_LEFT
1403 : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );
1405 this._run( direction, Math.abs( target ) - 1 );
1409 add: function ( item, index ) {
1414 if ( typeof item === "string" ) {
1415 item = { "src" : item };
1419 if ( typeof index !== "number" && index < 0
1420 && index >= this._imageList.length ) {
1424 this._imageList.splice( index, 0, item );
1430 remove: function ( index ) {
1432 if ( typeof index !== "number" && index < 0
1433 && index >= this._imageList.length ) {
1437 this._imageList.splice( index, 1 );
1443 clearThumbnailCache: function () {
1444 if ( !this._nodes || ( this._nodes.length <= 0 ) ) {
1449 for ( i = 0; i < this._imageList.length; i += 1 ) {
1450 url = this._imageList[i].src;
1451 $.imageloader.removeThumbnail( url );
1455 empty: function () {
1456 this._imageList = [];
1460 length: function () {
1461 return this._imageList.length;
1465 $( document ).on( "pagecreate create", function ( e ) {
1466 $( ":jqmData(role='gallery3d')" ).gallery3d();
1467 }).on( "pagechange", function ( e ) {
1468 $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1471 $( window ).on( "resize orientationchange", function ( e ) {
1472 $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1475 } ( jQuery, document, window ) );
1477 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
1479 //>>excludeEnd("jqmBuildExclude");