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.0, 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.0 (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 ) {
717 canvas = canvas || self._canvas;
719 $( self._nodes ).each( function ( i ) {
720 var node = self._nodes[i];
721 gl.deleteTexture( node.texture );
726 gl.deleteBuffer( self._positionBuffer );
727 self._positionBuffer = null;
728 gl.deleteBuffer( self._textureCoordBuffer );
729 self._textureCoordBuffer = null;
730 gl.deleteBuffer( self._normalVectorBuffer );
731 self._normalVectorBuffer = null;
733 $.webgl.shader.deleteShaders( gl );
734 gl.deleteProgram( self._shaderProgram );
735 self._shaderProgram = null;
737 self._gl = gl = null;
740 _addBehavier : function () {
743 canvas = self._canvas,
744 touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),
745 touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",
746 touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",
747 touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";
749 canvas.on( "webglcontextlost", function ( e ) {
751 }).on( "webglcontextrestored", function ( e ) {
753 }).on( touchStartEvt, function ( e ) {
757 deltas = [ deltaMaxSteps ],
758 deltaTimes = [ deltaMaxSteps ],
761 dragDirection = false,
767 if ( self._imageList.length <= 1 ) {
773 startX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
776 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
778 deltaTimes[i] = $.now();
783 view.on( touchMoveEvt, function ( e ) {
789 x = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
792 deltas[deltaIndex] = x;
793 deltaTimes[deltaIndex] = $.now();
794 interval = deltaTimes[deltaIndex] - prevTime;
796 deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;
798 // Validation of drag
799 if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {
800 if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {
802 dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;
805 dragValue += Math.abs( dx ) / 100;
806 if ( dragValue >= 1 ) {
807 self._setPosition( self._ANIMATION_END, dragDirection );
810 self._setPosition( dragValue, dragDirection );
816 }).on( touchEndEvt, function ( e ) {
832 // Validation of swipe
833 baseTime = $.now() - self._swipeThresholdOfBasetimeGap;
834 lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
837 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
838 index = ( deltaIndex + i ) % deltaMaxSteps;
839 if ( deltaTimes[index] > baseTime ) {
850 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
851 previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;
852 if ( deltaTimes[previous] < deltaTimes[recent] ) {
856 // too slow or too fast
857 if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {
863 baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );
864 fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];
865 if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {
868 velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );
871 if ( isSwipe && velocityX ) {
872 direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;
873 self._run( direction, Math.abs( velocityX ), dragValue );
874 } else if ( dragDirection !== 0 && dragValue ) {
875 self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );
878 view.unbind( ".gallery3d" );
879 }).on( touchLeaveEvt, function ( e ) {
880 view.trigger( touchEndEvt );
885 // ----------------------------------------------------------
887 // ----------------------------------------------------------
888 _initGL: function ( canvas ) {
890 mat4 = glMatrix.mat4,
893 gl = getContext3D( canvas );
898 gl.enable( gl.BLEND );
899 gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
901 gl.enable( gl.DEPTH_TEST );
902 gl.depthFunc( gl.LEQUAL );
904 canvas.width = self._VIEWPORT_WIDTH;
905 canvas.height = self._VIEWPORT_HEIGHT;
906 gl.viewportWidth = canvas.width;
907 gl.viewportHeight = canvas.height;
908 gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );
909 self._pMatrix = mat4.create();
910 mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );
912 gl.clearColor( 0.15, 0.15, 0.15, 1.0 );
913 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
918 _initShader : function ( gl ) {
922 shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );
923 gl.useProgram( shaderProgram );
925 shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );
926 gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );
928 shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );
929 gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );
931 // Set light normal vectors for lighting~
932 shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );
933 gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );
935 shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");
936 shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");
937 shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");
939 // Set light variables~
940 shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");
941 shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");
942 shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");
943 shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");
944 shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");
946 return shaderProgram;
949 _initBuffers: function ( gl, shaderProgram ) {
957 maxDrawLength = self._MAX_ITEM_COUNT;
959 for ( i = 0; i < self._imageList.length + 1; i += 1 ) {
960 nodes[i] = new Node();
961 $.merge( vertices, nodes[i].vertices );
962 $.merge( textureCoords, nodes[i].textureCoords );
963 $.merge( normalVectors, nodes[i].normalVectors );
965 nodes[i].textureBuffer = gl.createBuffer();
966 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );
968 nodes[i].meshOrder = [
969 mashBase, mashBase + 1, mashBase + 2,
970 mashBase + 2, mashBase + 3, mashBase
972 gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );
973 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory
974 nodes[i].textureBufferItemSize = 6;
977 self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );
978 self._positionBuffer.itemSize = 3;
980 self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );
981 self._textureCoordBuffer.itemSize = 2;
983 self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light
984 self._normalVectorBuffer.itemSize = 3;
987 gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );
989 gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );
994 // ----------------------------------------------------------
996 // ----------------------------------------------------------
997 _initTextures: function ( gl, nodes ) {
1000 $( nodes ).each( function ( i ) {
1001 var node = nodes[i],
1004 if ( !self._imageList[i] ) {
1008 url = self._imageList[i].src;
1009 node.texture = gl.createTexture();
1010 self._loadImage( url, i, i, gl, nodes );
1014 _loadImage: function ( url, i, imageID, gl, nodes ) {
1020 gl = gl || self._gl;
1021 nodes = nodes || self._nodes;
1022 isMipmap = isMipmap || false;
1024 node.image = node.image || new Image();
1026 $( node.image ).one( "load", function ( e ) {
1027 self._bindTexture( gl, node, this, isMipmap );
1028 node.imageID = imageID;
1030 if ( !self._animationID ) {
1031 self._setPosition( 0, 0 );
1035 if ( self.options.thumbnailCache ) {
1036 $.imageloader.getThumbnail( url, function ( result ) {
1037 if ( result === "NOT_FOUND_ERR" ) {
1038 $.imageloader.setThumbnail( url, function ( result ) {
1039 if ( result && result.length > 30 ) {
1040 node.image.src = result;
1043 node.image.src = url;
1046 } else if ( result && result.length > 30 ) {
1047 node.image.src = result;
1050 node.image.src = url;
1054 node.image.src = url;
1058 _bindTexture: function ( gl, node, image, isMipmap ) {
1059 if ( !node || !node.texture ) {
1063 gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
1065 gl.bindTexture( gl.TEXTURE_2D, node.texture );
1066 gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
1069 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1070 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );
1071 gl.generateMipmap( gl.TEXTURE_2D );
1073 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1074 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
1077 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
1078 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
1080 node.texture.loaded = true;
1082 // release texture memory
1083 gl.bindTexture( gl.TEXTURE_2D, null );
1086 // ----------------------------------------------------------
1088 // ----------------------------------------------------------
1089 _setPosition: function ( progress, direction ) {
1091 mat4 = glMatrix.mat4,
1092 nodes = self._nodes,
1093 imageList = self._imageList,
1094 imageListLength = imageList.length,
1095 itemCount = self._MAX_ITEM_COUNT,
1096 displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,
1108 nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;
1110 if ( !nodes[i].level ) {
1111 nodes[i].level = displayLength;
1114 for ( i = 0; i < displayLength; i += 1 ) {
1115 if ( !nodes[i].mvMatrix ) {
1116 nodes[i].mvMatrix = mat4.create();
1119 if ( direction > 0 && nodes[i].level >= displayLength ) {
1123 current = path.levels[nodes[i].level];
1124 nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;
1125 next = path.levels[nextLevel];
1127 if ( imageListLength > itemCount ) {
1128 if ( direction > 0 && nextLevel === 1
1129 && self._firstImageNumber !== nodes[i].imageID ) {
1130 self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );
1131 } else if ( direction < 0 && nextLevel === nextLevelLenth - 1
1132 && self._lastImageNumber !== nodes[i].imageID ) {
1133 self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );
1137 mat4.identity( nodes[i].mvMatrix );
1138 mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );
1139 mat4.rotate( nodes[i].mvMatrix, degreeToRadian( 19 ), [1, 0, 0] );
1141 t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );
1143 if ( progress >= self._ANIMATION_END ) {
1144 nodes[i].level = nextLevel || displayLength;
1145 t = path.levels[nodes[i].level];
1148 if ( ( progress < self._ANIMATION_END )
1149 && ( direction <= 0 && nodes[i].level < 1 ) ) {
1150 nodes[i].drawable = false;
1152 nodes[i].drawable = true;
1155 if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {
1156 self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );
1159 position = path.getPosition( t );
1160 angle = path.getAngle( t );
1162 mat4.translate( nodes[i].mvMatrix, position );
1163 mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );
1166 if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {
1167 self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;
1168 if ( self._firstImageNumber < 0 ) {
1169 self._firstImageNumber = imageListLength - 1;
1172 self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;
1173 if ( self._lastImageNumber < 0 ) {
1174 self._lastImageNumber = imageListLength - 1;
1180 _drawScene: function () {
1181 if ( !this._gl || !this._shaderProgram ) {
1187 shaderProgram = self._shaderProgram,
1188 nodes = self._nodes,
1189 nodesLength = nodes.length,
1192 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
1194 gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );
1195 gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1197 gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );
1198 gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1200 gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );
1201 gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1203 for ( i = 0; i < nodesLength; i += 1 ) {
1204 if ( nodes[i].drawable ) {
1205 self._drawElement( self._pMatrix, nodes[i] );
1210 _drawElement: function ( perspectiveMatrix, targetNode ) {
1213 vec3 = glMatrix.vec3,
1214 mat3 = glMatrix.mat3,
1215 mat4 = glMatrix.mat4,
1216 shaderProgram = self._shaderProgram,
1217 moveMatrix = targetNode.mvMatrix,
1218 texture = targetNode.texture,
1219 meshIndexBuffer = targetNode.textureBuffer,
1220 meshIndexBufferItemSize = targetNode.textureBufferItemSize,
1221 lightPositions = self._lightsPositionStack,
1225 if ( !moveMatrix ) {
1229 gl.activeTexture( gl.TEXTURE0 );
1230 if ( texture && texture.loaded ) {
1231 gl.bindTexture( gl.TEXTURE_2D, texture );
1233 gl.uniform1i( shaderProgram.sampleUniform, 0 );
1235 LightDir = vec3.create();
1236 vec3.normalize( lightPositions[0], LightDir );
1237 vec3.scale( LightDir, -8 );
1238 gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );
1240 vec3.normalize( lightPositions[1], LightDir );
1241 vec3.scale( LightDir, -1 );
1242 gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );
1243 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );
1245 gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );
1246 gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );
1248 normalMatrix = mat3.create();
1249 mat4.toInverseMat3( moveMatrix, normalMatrix );
1250 mat3.transpose( normalMatrix );
1251 gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );
1253 gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );
1255 // release buffer memory
1256 gl.bindBuffer( gl.ARRAY_BUFFER, null );
1257 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
1259 // release texture memory
1260 gl.bindTexture( gl.TEXTURE_2D, null );
1263 // ----------------------------------------------------------
1265 // ----------------------------------------------------------
1266 _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {
1272 easingType = easingType || "linear";
1273 startValue = startValue || 0;
1274 _removeCount = _removeCount || 0;
1276 if ( self._sumTime >= duration ) {
1277 self._setPosition( self._ANIMATION_END, direction );
1282 if ( self._startTime === 0 ) {
1283 self._startTime = timeNow;
1285 self._sumTime = timeNow - self._startTime;
1286 progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );
1287 removeCount = parseInt( Math.abs( progress ), 10 );
1289 if ( _removeCount !== removeCount ) {
1290 self._setPosition( self._ANIMATION_END, direction );
1291 _removeCount = removeCount;
1293 if ( ( repeatCount - _removeCount ) >= 0 ) {
1294 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1301 self._setPosition( progress - _removeCount, direction );
1304 self._animationID = requestAnimationFrame( function () {
1305 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1309 _run: function ( direction, repeatCount, startValue ) {
1311 repeat = repeatCount || 0,
1312 duration = self._DURATION_DEFAULT * ( repeat + 1 );
1314 if ( self._imageList.length <= 1 ) {
1318 startValue = startValue || 0;
1319 duration = ( duration >= 0 ) ? duration : 0;
1321 if ( self._animationID ) {
1322 self._setPosition( self._ANIMATION_END, direction );
1326 self._animate( "easeOutExpo", duration, direction, repeat, startValue );
1329 _reset: function () {
1330 if ( !this._canvas || !this._gl ) {
1339 _stop: function () {
1340 if ( this._animationID ) {
1341 cancelAnimationFrame( this._animationID );
1343 this._animationID = 0;
1345 this._startTime = 0;
1350 this._run( this._DIRECTION_LEFT , 0 );
1354 this._run( this._DIRECTION_RIGHT, 0 );
1357 refresh: function () {
1358 var view = this.element,
1359 canvas = view.find( "canvas.ui-gallery3d-canvas" );
1361 if ( canvas.width() !== view.width() ) {
1362 canvas.width( view.width() );
1365 if ( !this._animationID ) {
1366 this._setPosition( 0, 0 );
1370 select: function ( index ) {
1371 var nodes = this._nodes,
1379 if ( index && this._animationID ) {
1383 for ( i in nodes ) {
1384 if ( nodes[i].level === 1 ) {
1385 object = this._imageList[ nodes[i].imageID ];
1386 imageID = nodes[i].imageID;
1395 if ( index < 0 && index >= this._imageList.length ) {
1399 target = index - imageID;
1400 direction = ( target > 0 ) ? this._DIRECTION_LEFT
1401 : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );
1403 this._run( direction, Math.abs( target ) - 1 );
1407 add: function ( item, index ) {
1412 if ( typeof item === "string" ) {
1413 item = { "src" : item };
1417 if ( typeof index !== "number" && index < 0
1418 && index >= this._imageList.length ) {
1422 this._imageList.splice( index, 0, item );
1428 remove: function ( index ) {
1430 if ( typeof index !== "number" && index < 0
1431 && index >= this._imageList.length ) {
1435 this._imageList.splice( index, 1 );
1441 clearThumbnailCache: function () {
1442 if ( !this._nodes || ( this._nodes.length <= 0 ) ) {
1447 for ( i = 0; i < this._imageList.length; i += 1 ) {
1448 url = this._imageList[i].src;
1449 $.imageloader.removeThumbnail( url );
1453 empty: function () {
1454 this._imageList = [];
1458 length: function () {
1459 return this._imageList.length;
1463 $( document ).on( "pagecreate create", function ( e ) {
1464 $( ":jqmData(role='gallery3d')" ).gallery3d();
1465 }).on( "pagechange", function ( e ) {
1466 $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1469 $( window ).on( "resize orientationchange", function ( e ) {
1470 $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1473 } ( jQuery, document, window ) );
1475 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
1477 //>>excludeEnd("jqmBuildExclude");