3 /* ***************************************************************************
6 Version 1.1, April, 2013
8 http://floralicense.org/license/
10 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
14 "License" shall mean the terms and conditions for use, reproduction,
15 and distribution as defined by Sections 1 through 9 of this document.
17 "Licensor" shall mean the copyright owner or entity authorized by
18 the copyright owner that is granting the License.
20 "Legal Entity" shall mean the union of the acting entity and
21 all other entities that control, are controlled by, or are
22 under common control with that entity. For the purposes of
23 this definition, "control" means (i) the power, direct or indirect,
24 to cause the direction or management of such entity,
25 whether by contract or otherwise, or (ii) ownership of fifty percent (50%)
26 or more of the outstanding shares, or (iii) beneficial ownership of
29 "You" (or "Your") shall mean an individual or Legal Entity
30 exercising permissions granted by this License.
32 "Source" form shall mean the preferred form for making modifications,
33 including but not limited to software source code, documentation source,
34 and configuration files.
36 "Object" form shall mean any form resulting from mechanical
37 transformation or translation of a Source form, including but
38 not limited to compiled object code, generated documentation,
39 and conversions to other media types.
41 "Work" shall mean the work of authorship, whether in Source or Object form,
42 made available under the License, as indicated by a copyright notice
43 that is included in or attached to the work (an example is provided
44 in the Appendix below).
46 "Derivative Works" shall mean any work, whether in Source or Object form,
47 that is based on (or derived from) the Work and for which the editorial
48 revisions, annotations, elaborations, or other modifications represent,
49 as a whole, an original work of authorship. For the purposes of this License,
50 Derivative Works shall not include works that remain separable from,
51 or merely link (or bind by name) to the interfaces of, the Work and
52 Derivative Works thereof.
54 "Contribution" shall mean any work of authorship, including the original
55 version of the Work and any modifications or additions to that Work or
56 Derivative Works thereof, that is intentionally submitted to Licensor
57 for inclusion in the Work by the copyright owner or by an individual or
58 Legal Entity authorized to submit on behalf of the copyright owner.
59 For the purposes of this definition, "submitted" means any form of
60 electronic, verbal, or written communication sent to the Licensor or
61 its representatives, including but not limited to communication on
62 electronic mailing lists, source code control systems, and issue
63 tracking systems that are managed by, or on behalf of, the Licensor
64 for the purpose of discussing and improving the Work, but excluding
65 communication that is conspicuously marked or otherwise designated
66 in writing by the copyright owner as "Not a Contribution."
68 "Contributor" shall mean Licensor and any individual or Legal Entity
69 on behalf of whom a Contribution has been received by Licensor and
70 subsequently incorporated within the Work.
72 "Tizen Certified Platform" shall mean a software platform that complies
73 with the standards set forth in the Tizen Compliance Specification
74 and passes the Tizen Compliance Tests as defined from time to time
75 by the Tizen Technical Steering Group and certified by the Tizen
76 Association or its designated agent.
78 2. Grant of Copyright License. Subject to the terms and conditions of
79 this License, each Contributor hereby grants to You a perpetual,
80 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
81 copyright license to reproduce, prepare Derivative Works of,
82 publicly display, publicly perform, sublicense, and distribute the
83 Work and such Derivative Works in Source or Object form.
85 3. Grant of Patent License. Subject to the terms and conditions of
86 this License, each Contributor hereby grants to You a perpetual,
87 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
88 (except as stated in this section) patent license to make, have made,
89 use, offer to sell, sell, import, and otherwise transfer the Work
90 solely as incorporated into a Tizen Certified Platform, where such
91 license applies only to those patent claims licensable by such
92 Contributor that are necessarily infringed by their Contribution(s)
93 alone or by combination of their Contribution(s) with the Work solely
94 as incorporated into a Tizen Certified Platform to which such
95 Contribution(s) was submitted. If You institute patent litigation
96 against any entity (including a cross-claim or counterclaim
97 in a lawsuit) alleging that the Work or a Contribution incorporated
98 within the Work constitutes direct or contributory patent infringement,
99 then any patent licenses granted to You under this License for that
100 Work shall terminate as of the date such litigation is filed.
102 4. Redistribution. You may reproduce and distribute copies of the
103 Work or Derivative Works thereof pursuant to the copyright license
104 above, in any medium, with or without modifications, and in Source or
105 Object form, provided that You meet the following conditions:
107 1. You must give any other recipients of the Work or Derivative Works
108 a copy of this License; and
109 2. You must cause any modified files to carry prominent notices stating
110 that You changed the files; and
111 3. You must retain, in the Source form of any Derivative Works that
112 You distribute, all copyright, patent, trademark, and attribution
113 notices from the Source form of the Work, excluding those notices
114 that do not pertain to any part of the Derivative Works; and
115 4. If the Work includes a "NOTICE" text file as part of its distribution,
116 then any Derivative Works that You distribute must include a readable
117 copy of the attribution notices contained within such NOTICE file,
118 excluding those notices that do not pertain to any part of
119 the Derivative Works, in at least one of the following places:
120 within a NOTICE text file distributed as part of the Derivative Works;
121 within the Source form or documentation, if provided along with the
122 Derivative Works; or, within a display generated by the Derivative Works,
123 if and wherever such third-party notices normally appear.
124 The contents of the NOTICE file are for informational purposes only
125 and do not modify the License.
127 You may add Your own attribution notices within Derivative Works
128 that You distribute, alongside or as an addendum to the NOTICE text
129 from the Work, provided that such additional attribution notices
130 cannot be construed as modifying the License. You may add Your own
131 copyright statement to Your modifications and may provide additional or
132 different license terms and conditions for use, reproduction, or
133 distribution of Your modifications, or for any such Derivative Works
134 as a whole, provided Your use, reproduction, and distribution of
135 the Work otherwise complies with the conditions stated in this License.
137 5. Submission of Contributions. Unless You explicitly state otherwise,
138 any Contribution intentionally submitted for inclusion in the Work
139 by You to the Licensor shall be under the terms and conditions of
140 this License, without any additional terms or conditions.
141 Notwithstanding the above, nothing herein shall supersede or modify
142 the terms of any separate license agreement you may have executed
143 with Licensor regarding such Contributions.
145 6. Trademarks. This License does not grant permission to use the trade
146 names, trademarks, service marks, or product names of the Licensor,
147 except as required for reasonable and customary use in describing the
148 origin of the Work and reproducing the content of the NOTICE file.
150 7. Disclaimer of Warranty. Unless required by applicable law or
151 agreed to in writing, Licensor provides the Work (and each
152 Contributor provides its Contributions) on an "AS IS" BASIS,
153 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
154 implied, including, without limitation, any warranties or conditions
155 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
156 PARTICULAR PURPOSE. You are solely responsible for determining the
157 appropriateness of using or redistributing the Work and assume any
158 risks associated with Your exercise of permissions under this License.
160 8. Limitation of Liability. In no event and under no legal theory,
161 whether in tort (including negligence), contract, or otherwise,
162 unless required by applicable law (such as deliberate and grossly
163 negligent acts) or agreed to in writing, shall any Contributor be
164 liable to You for damages, including any direct, indirect, special,
165 incidental, or consequential damages of any character arising as a
166 result of this License or out of the use or inability to use the
167 Work (including but not limited to damages for loss of goodwill,
168 work stoppage, computer failure or malfunction, or any and all
169 other commercial damages or losses), even if such Contributor
170 has been advised of the possibility of such damages.
172 9. Accepting Warranty or Additional Liability. While redistributing
173 the Work or Derivative Works thereof, You may choose to offer,
174 and charge a fee for, acceptance of support, warranty, indemnity,
175 or other liability obligations and/or rights consistent with this
176 License. However, in accepting such obligations, You may act only
177 on Your own behalf and on Your sole responsibility, not on behalf
178 of any other Contributor, and only if You agree to indemnify,
179 defend, and hold each Contributor harmless for any liability
180 incurred by, or claims asserted against, such Contributor by reason
181 of your accepting any such warranty or additional liability.
183 END OF TERMS AND CONDITIONS
185 APPENDIX: How to apply the Flora License to your work
187 To apply the Flora License to your work, attach the following
188 boilerplate notice, with the fields enclosed by brackets "[]"
189 replaced with your own identifying information. (Don't include
190 the brackets!) The text should be enclosed in the appropriate
191 comment syntax for the file format. We also recommend that a
192 file or class name and description of purpose be included on the
193 same "printed page" as the copyright notice for easier
194 identification within third-party archives.
196 Copyright [yyyy] [name of copyright owner]
198 Licensed under the Flora License, Version 1.1 (the "License");
199 you may not use this file except in compliance with the License.
200 You may obtain a copy of the License at
202 http://floralicense.org/license/
204 Unless required by applicable law or agreed to in writing, software
205 distributed under the License is distributed on an "AS IS" BASIS,
206 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
207 See the License for the specific language governing permissions and
208 limitations under the License.
211 * Authors: Hyunsook Park <hyunsook.park@samsung.com>
212 * Wonseop Kim <wonseop.kim@samsung.com>
216 * The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
217 * To improve performance, the size of image(s) displayed on the screen should be a square(under
218 * 128X128 pixel) as possible. But if a user can't resize the images, this widget supports an image
219 * resizing feature and he/she can use it with "data-thumbnail-cache" option. ("data-thumbnail-cache"
220 * option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
221 * So when a gallery3D widget is re-launched, the widget reuse the storage and a user can improve
222 * launching time. A browser or web runtime engine should support "Web Storage" feature to use that
227 * data-thumbnail-cache : Determines whether to cache and resize images.
232 * : This method moves each image forward one by one.
234 * : This method moves each image backward one by one.
235 * select ( [number] )
236 * : When the "select" method is called with an argument, the method selects the image of given index.
237 * If the method is called with no argument, it will return the Javascript object having "src"
238 * attribute having the selected image's URL.
239 * add ( object or string [, number] )
240 * This method adds an image to Gallery3D widget.
241 * If the second argument isn't inputted, the image is added at the 0th position.
242 * remove ( [number] )
243 * : This method deletes an image from Gallery3d widget.
244 * The argument defines the index of the image to be deleted.
245 * If an argument isn't inputted, it removes current image.
246 * clearThumbnailCache ( void )
247 * : This method clears the cache data of all images when thumbnailCache option is set as 'true'.
249 * : This method updates and redraws current widget.
251 * : This method removes all of images from Gallery3D widget.
253 * : This method gets the number of images.
257 * select : Triggered when an image is selected.
262 * $( "#gallery3d" ).on( "gallery3dcreate", function () {
263 * $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
266 * <div id="gallery3d" data-role="gallery3d"></div>
271 The gallery3d widget displays images along a curved path on a 3-dimensional coordinate system.
272 <br/><br/>To add an gallery3d widget to the application, use the following code:
275 $( "#gallery3d" ).on( "gallery3dcreate", function () {
276 $( "#gallery3d" ).gallery3d( "add", "01.jpg" );
279 <div id="gallery3d" data-role="gallery3d"></div>
282 @property {Boolean} data-thumbnail-cache
283 Determines whether to cache and resize images.
284 To improve performance, the size of image(s) displayed on the screen should be a square (under 128X128 pixels).
285 "data-thumbnail-cache" option resizes the gallery images under 128x128 pixels and stores the images on a local storage.
286 So when a gallery3D widget is re-launched, the widget reuses the storage and the launching time can be improved.
287 A browser or web runtime engine must support "Web Storage" feature to use this option.
291 Triggered when an image is selected.
294 $( "#gallery3d" ).on( "gallery3dcreate", function () {
295 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
296 .gallery3d( "add", { src: "2.jpg" } )
297 .gallery3d( "add", { src: "3.jpg" } );
298 }).on( "select", function ( event, data, index ) {
299 // Handle the select event
300 var urlOfImage = data.src, indexOfImage = index;
303 <div id="gallery3d" data-role="gallery3d"></div>
307 This method moves each image forward one by one.
310 $( "#gallery3d" ).on( "gallery3dcreate", function () {
311 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
312 .gallery3d( "add", { src: "2.jpg" } )
313 .gallery3d( "add", { src: "3.jpg" } )
314 .gallery3d( "next" );
317 <div id="gallery3d" data-role="gallery3d"></div>
321 This method moves each image backward one by one.
324 $( "#gallery3d" ).on( "gallery3dcreate", function () {
325 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
326 .gallery3d( "add", { src: "2.jpg" } )
327 .gallery3d( "add", { src: "3.jpg" } )
328 .gallery3d( "prev" );
331 <div id="gallery3d" data-role="gallery3d"></div>
335 When the "select" method is called with an argument, the method selects the image of given index.
336 If the method is called with no argument, it will return the Javascript object having "src" attribute having the selected image's URL.
339 $( "#gallery3d" ).on( "gallery3dcreate", function () {
340 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
341 .gallery3d( "add", { src: "2.jpg" } )
342 .gallery3d( "add", { src: "3.jpg" } );
343 var selectedImage = $("#gallery3d"). gallery3d( "select" );
344 // selectedImage = { src: "3.jpg" };
347 <div id="gallery3d" data-role="gallery3d"></div>
351 This method adds an image to Gallery3D widget.
352 The first argument is a Javascript object having a "src" attribute or a string of image's path.
353 The second argument is an index of images.
354 If second argument isn't inputted, the image is added at the 0th position.
357 $( "#gallery3d" ).on( "gallery3dcreate", function () {
358 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
359 .gallery3d( "add", "2.jpg", 1 );
362 <div id="gallery3d" data-role="gallery3d"></div>
366 This method deletes an image from Gallery3d widget.
367 The argument defines the index of the image to be deleted.
368 If an argument isn't inputted, it removes current image.
371 $( "#gallery3d" ).on( "gallery3dcreate", function () {
372 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
373 .gallery3d( "add", { src: "2.jpg" } )
374 .gallery3d( "add", { src: "3.jpg" } );
376 $( "#gallery3d" ).gallery3d( "remove" );
377 $( "#gallery3d" ).gallery3d( "remove", 1 );
380 <div id="gallery3d" data-role="gallery3d"></div>
383 @method clearThumbnailCache
384 This method clears the cache data of all images when thumbnailCache option is set as 'true'
387 $( "#gallery3d" ).on( "gallery3dcreate", function () {
388 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
389 .gallery3d( "add", { src: "2.jpg" } )
390 .gallery3d( "add", { src: "3.jpg" } );
392 $( "#gallery3d" ).gallery3d( "clearThumbnailCache" );
395 <div id="gallery3d" data-role="gallery3d" data-thumbnail-cache="true"></div>
399 This method updates and redraws current widget.
402 $( "#gallery3d" ).on( "gallery3dcreate", function () {
403 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
404 .gallery3d( "add", { src: "2.jpg" } )
405 .gallery3d( "add", { src: "3.jpg" } );
407 $( "#gallery3d" ).gallery3d( "refresh" );
410 <div id="gallery3d" data-role="gallery3d"></div>
414 This method removes all of images from Gallery3D widget.
417 $( "#gallery3d" ).on( "gallery3dcreate", function () {
418 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
419 .gallery3d( "add", { src: "2.jpg" } )
420 .gallery3d( "add", { src: "3.jpg" } );
422 $( "#gallery3d" ).gallery3d( "empty" );
425 <div id="gallery3d" data-role="gallery3d"></div>
429 This method gets the number of images.
432 $( "#gallery3d" ).on( "gallery3dcreate", function () {
433 $( "#gallery3d" ).gallery3d( "add", { src: "1.jpg" } )
434 .gallery3d( "add", { src: "2.jpg" } )
435 .gallery3d( "add", { src: "3.jpg" } );
437 var imagesLength = $( "#gallery3d" ).gallery3d( "length" );
441 <div id="gallery3d" data-role="gallery3d"></div>
444 ( function ( $, document, window, undefined ) {
452 this.textureCoords = [
458 this.normalVectors = [
465 this.textureBuffer = null;
466 this.textureBufferItemSize = 0;
468 this.mvMatrix = null;
470 this.targetLevel = 0;
471 this.drawable = false;
476 var isPreInitailization = false,
482 preInitialize = function () {
483 if ( isPreInitailization ) {
487 window.initGlMatrix( glMatrix );
490 "attribute vec3 aVertexPosition;",
491 "attribute vec2 aTextureCoord;",
492 "attribute vec3 aVertexNormal;",
493 "uniform mat4 uMoveMatrix;",
494 "uniform mat4 uPerspectiveMatrix;",
495 "uniform mat3 nNormalMatrix;",
496 "uniform vec3 uAmbientColor;",
497 "uniform vec3 uLightDirection;",
498 "uniform vec3 uDirectionColor;",
499 "uniform vec3 uLightDirection_first;",
500 "uniform vec3 uLightDirection_second;",
501 "varying vec2 vTextureCoord;",
502 "varying vec3 vLightWeight;",
503 "varying vec4 vFogWeight;",
506 " vec4 v_Position = uMoveMatrix * vec4(aVertexPosition, 1.0);",
507 " gl_Position = uPerspectiveMatrix * v_Position;",
508 " vTextureCoord = aTextureCoord;",
509 " float fog = 1.0 - ((gl_Position.z + 1.5) / 60.0);",
510 " vFogWeight = clamp( vec4( fog, fog, fog, 1.0), 0.6, 1.0);",
511 " vec3 transNormalVector = nNormalMatrix * aVertexNormal;",
513 " float vLightWeightFirst = 0.0;",
514 " float vLightWeightSecond = max( dot(transNormalVector, uLightDirection_second), 0.0 );",
516 " vLightWeight = uAmbientColor + uDirectionColor * vLightWeightSecond;",
521 "precision mediump float;",
522 "varying vec2 vTextureCoord;",
523 "varying vec3 vLightWeight;",
524 "uniform sampler2D uSampler;",
525 "varying vec4 vFogWeight;",
528 " vec4 TextureColor;",
529 " if ( vTextureCoord.s <= 0.01 || vTextureCoord.s >= 0.99 || vTextureCoord.t <= 0.01 || vTextureCoord.t >= 0.99 ) {",
530 " TextureColor = vec4(1.0, 1.0, 1.0, 0.5);",
532 " TextureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));",
534 " TextureColor *= vFogWeight;",
535 " gl_FragColor = vec4(TextureColor.rgb * vLightWeight, TextureColor.a);",
539 GlArray32 = ( typeof window.Float32Array !== "undefined" ?
540 window.Float32Array :
541 ( typeof window.WebGLFloatArray !== "undefined" ? window.WebGLFloatArray : Array ) );
543 GlArray16 = ( typeof window.Uint16Array !== "undefined" ? window.Uint16Array : Array );
545 isPreInitailization = true;
547 degreeToRadian = function ( degree ) {
548 return degree * Math.PI / 180;
550 getContext3D = function ( canvas ) {
552 contextNames = [ "experimental-webgl", "webkit-3d", "webgl", "moz-webgl" ];
554 for ( i = 0; i < contextNames.length; i += 1 ) {
556 gl = canvas.getContext( contextNames[i] );
561 $( canvas ).html( "Unfortunately, there's a WebGL compatibility problem. </br> You may want to check your system settings." );
567 requestAnimationFrame = function ( callback ) {
568 var id = window.setTimeout( callback, 1000 / 60 );
571 cancelAnimationFrame = function ( id ) {
572 window.clearTimeout( id );
575 $.widget( "tizen.gallery3d", $.mobile.widget, {
577 thumbnailCache: false
582 _DURATION_DEFAULT: 300,
583 _DURATION_FIRST: 1600,
584 _VIEWPORT_WIDTH: 1024,
585 _VIEWPORT_HEIGHT: 456,
587 _DIRECTION_RIGHT: +1,
590 _shaderProgram : null,
591 _positionBuffer : null,
592 _textureCoordBuffer : null,
593 _normalVectorBuffer : null,
600 _lightsPositionStack : [
601 [0.0, 0.0, -1.0], // back
602 [-0.2, 0.0, 0.7] // front
605 _swipeThresholdOfBasetimeGap: ( $.support.touch ? 30 : 70 ),
606 _swipeThresholdOfSensitivity: ( $.support.touch ? 2.0 : 10.0 ),
610 _firstImageNumber: 0,
613 _create: function () {
616 option = self.options;
620 self._canvas = $( "<canvas class='ui-gallery3d-canvas'></canvas>" );
622 view.addClass( "ui-gallery3d" ).append( self._canvas );
625 self._dragInterval = 1000 / 30; // 30fps
627 $.each( self.options, function ( key, value ) {
628 self.options[ key ] = undefined;
629 self._setOption( key, value );
633 destroy: function () {
635 $.mobile.widget.prototype.destroy.call( this );
638 _setOption: function ( key, value ) {
640 case "thumbnailCache" :
641 if ( typeof value === "string" ) {
642 value = ( value === "true" ) ? true : false;
650 $.mobile.widget.prototype._setOption.call( this, key, value );
653 _init: function ( canvas ) {
657 [-12, 0, -40], // contorl Point of Point1
658 [24, 0, -9], // contorl Point of Point2
663 canvas = canvas || self._canvas;
669 self._gl = self._gl || self._initGL( canvas[0] );
674 if ( !self._imageList ) {
678 self._shaderProgram = self._shaderProgram || self._initShader( self._gl );
679 if ( !self._shaderProgram ) {
683 if ( self._imageList.length > self._MAX_ITEM_COUNT ) {
684 self._firstImageNumber = self._imageList.length - 1;
685 self._lastImageNumber = self._MAX_ITEM_COUNT - 1;
688 self._nodes = self._initBuffers( self._gl, self._shaderProgram );
690 self._initTextures( self._gl, self._nodes );
692 self._path = $.motionpath( "bezier2d", {
694 maxLevel: self._MAX_ITEM_COUNT
696 for ( i = 0; i < self._nodes.length; i += 1 ) {
697 self._path.levels[i] = self._path.levels[i + 1] || 0;
698 self._nodes[i].level = i;
702 _final: function ( canvas ) {
712 canvas = canvas || self._canvas;
714 $( self._nodes ).each( function ( i ) {
715 var node = self._nodes[i];
716 gl.deleteTexture( node.texture );
721 gl.deleteBuffer( self._positionBuffer );
722 self._positionBuffer = null;
723 gl.deleteBuffer( self._textureCoordBuffer );
724 self._textureCoordBuffer = null;
725 gl.deleteBuffer( self._normalVectorBuffer );
726 self._normalVectorBuffer = null;
728 $.webgl.shader.deleteShaders( gl );
729 gl.deleteProgram( self._shaderProgram );
730 self._shaderProgram = null;
732 self._gl = gl = null;
735 _addBehavier : function () {
738 canvas = self._canvas,
739 touchStartEvt = ( $.support.touch ? "touchstart" : "mousedown" ),
740 touchMoveEvt = ( $.support.touch ? "touchmove" : "mousemove" ) + ".gallery3d",
741 touchEndEvt = ( $.support.touch ? "touchend" : "mouseup" ) + ".gallery3d",
742 touchLeaveEvt = ( $.support.touch ? "touchleave" : "mouseout" ) + ".gallery3d";
744 canvas.on( "webglcontextlost", function ( e ) {
746 }).on( "webglcontextrestored", function ( e ) {
748 }).on( touchStartEvt, function ( e ) {
752 deltas = [ deltaMaxSteps ],
753 deltaTimes = [ deltaMaxSteps ],
756 dragDirection = false,
762 if ( self._imageList.length <= 1 ) {
768 startX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
771 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
773 deltaTimes[i] = $.now();
778 view.on( touchMoveEvt, function ( e ) {
784 x = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
787 deltas[deltaIndex] = x;
788 deltaTimes[deltaIndex] = $.now();
789 interval = deltaTimes[deltaIndex] - prevTime;
791 deltaIndex = ( deltaIndex + 1 ) % deltaMaxSteps;
793 // Validation of drag
794 if ( Math.abs( dx ) >= 10 && interval >= self._dragInterval ) {
795 if ( dragDirection !== ( ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT ) ) {
797 dragDirection = ( dx < 0 ) ? self._DIRECTION_RIGHT : self._DIRECTION_LEFT;
800 dragValue += Math.abs( dx ) / 100;
801 if ( dragValue >= 1 ) {
802 self._setPosition( self._ANIMATION_END, dragDirection );
805 self._setPosition( dragValue, dragDirection );
811 }).on( touchEndEvt, function ( e ) {
827 // Validation of swipe
828 baseTime = $.now() - self._swipeThresholdOfBasetimeGap;
829 lastX = $.support.touch ? e.originalEvent.changedTouches[0].pageX : e.pageX;
832 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
833 index = ( deltaIndex + i ) % deltaMaxSteps;
834 if ( deltaTimes[index] > baseTime ) {
845 for ( i = 0; i < deltaMaxSteps; i += 1 ) {
846 previous = ( previous - 1 + deltaMaxSteps ) % deltaMaxSteps;
847 if ( deltaTimes[previous] < deltaTimes[recent] ) {
851 // too slow or too fast
852 if ( i === deltaMaxSteps || baseTime < deltaTimes[previous] ) {
858 baseTimeRatio = ( baseTime - deltaTimes[previous] ) / ( deltaTimes[recent] - deltaTimes[previous] );
859 fx = ( 1.0 - baseTimeRatio ) * deltas[previous] + baseTimeRatio * deltas[recent];
860 if ( Math.abs( fx - lastX ) < self._swipeThresholdOfSensitivity ) {
863 velocityX = parseInt( ( lastX - fx ) / ( $.now() - baseTime ), 10 );
866 if ( isSwipe && velocityX ) {
867 direction = ( velocityX < 0 ) ? self._DIRECTION_LEFT : self._DIRECTION_RIGHT;
868 self._run( direction, Math.abs( velocityX ), dragValue );
869 } else if ( dragDirection !== 0 && dragValue ) {
870 self._animate( null, self._DURATION_DEFAULT * ( 1 - dragValue ), dragDirection, 0, dragValue );
873 view.unbind( ".gallery3d" );
874 }).on( touchLeaveEvt, function ( e ) {
875 view.trigger( touchEndEvt );
880 // ----------------------------------------------------------
882 // ----------------------------------------------------------
883 _initGL: function ( canvas ) {
885 mat4 = glMatrix.mat4,
888 gl = getContext3D( canvas );
893 gl.enable( gl.BLEND );
894 gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA );
896 gl.enable( gl.DEPTH_TEST );
897 gl.depthFunc( gl.LEQUAL );
899 canvas.width = self._VIEWPORT_WIDTH;
900 canvas.height = self._VIEWPORT_HEIGHT;
901 gl.viewportWidth = canvas.width;
902 gl.viewportHeight = canvas.height;
903 gl.viewport( 0, 0, gl.viewportWidth, gl.viewportHeight );
904 self._pMatrix = mat4.create();
905 mat4.perspective( 40, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, self._pMatrix );
907 gl.clearColor( 0.15, 0.15, 0.15, 1.0 );
908 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
913 _initShader : function ( gl ) {
917 shaderProgram = $.webgl.shader.addShaderProgram( self._gl, VERTEX_SHADER, FRAGMENT_SHADER );
918 gl.useProgram( shaderProgram );
920 shaderProgram.vertexPositionAttr = gl.getAttribLocation( shaderProgram, "aVertexPosition" );
921 gl.enableVertexAttribArray( shaderProgram.vertexPositionAttr );
923 shaderProgram.textureCoordAttr = gl.getAttribLocation( shaderProgram, "aTextureCoord" );
924 gl.enableVertexAttribArray( shaderProgram.textureCoordAttr );
926 // Set light normal vectors for lighting~
927 shaderProgram.vertexNormalAttr = gl.getAttribLocation( shaderProgram, "aVertexNormal" );
928 gl.enableVertexAttribArray( shaderProgram.vertexNormalAttr );
930 shaderProgram.perspectiveMU = gl.getUniformLocation( shaderProgram, "uPerspectiveMatrix");
931 shaderProgram.transformMU = gl.getUniformLocation( shaderProgram, "uMoveMatrix");
932 shaderProgram.sampleUniform = gl.getUniformLocation( shaderProgram, "uSampler");
934 // Set light variables~
935 shaderProgram.normalMU = gl.getUniformLocation( shaderProgram, "nNormalMatrix");
936 shaderProgram.ambientColorU = gl.getUniformLocation( shaderProgram, "uAmbientColor");
937 shaderProgram.lightDirU_first = gl.getUniformLocation( shaderProgram, "uLightDirection_first");
938 shaderProgram.lightDirU_second = gl.getUniformLocation( shaderProgram, "uLightDirection_second");
939 shaderProgram.directionColorU = gl.getUniformLocation( shaderProgram, "uDirectionColor");
941 return shaderProgram;
944 _initBuffers: function ( gl, shaderProgram ) {
952 maxDrawLength = self._MAX_ITEM_COUNT;
954 for ( i = 0; i < self._imageList.length + 1; i += 1 ) {
955 nodes[i] = new Node();
956 $.merge( vertices, nodes[i].vertices );
957 $.merge( textureCoords, nodes[i].textureCoords );
958 $.merge( normalVectors, nodes[i].normalVectors );
960 nodes[i].textureBuffer = gl.createBuffer();
961 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, nodes[i].textureBuffer );
963 nodes[i].meshOrder = [
964 mashBase, mashBase + 1, mashBase + 2,
965 mashBase + 2, mashBase + 3, mashBase
967 gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new GlArray16( nodes[i].meshOrder ), gl.STATIC_DRAW );
968 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); // release buffer memory
969 nodes[i].textureBufferItemSize = 6;
972 self._positionBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( vertices ) );
973 self._positionBuffer.itemSize = 3;
975 self._textureCoordBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( textureCoords ) );
976 self._textureCoordBuffer.itemSize = 2;
978 self._normalVectorBuffer = $.webgl.buffer.attribBufferData( gl, new GlArray32( normalVectors ) ); // Vertex's normal vector for Direction light
979 self._normalVectorBuffer.itemSize = 3;
982 gl.uniform3f( shaderProgram.ambientColorU, 0.1, 0.1, 0.1 );
984 gl.uniform3f( shaderProgram.directionColorU, 1.0, 1.0, 1.0 );
989 // ----------------------------------------------------------
991 // ----------------------------------------------------------
992 _initTextures: function ( gl, nodes ) {
995 $( nodes ).each( function ( i ) {
999 if ( !self._imageList[i] ) {
1003 url = self._imageList[i].src;
1004 node.texture = gl.createTexture();
1005 self._loadImage( url, i, i, gl, nodes );
1009 _loadImage: function ( url, i, imageID, gl, nodes ) {
1015 gl = gl || self._gl;
1016 nodes = nodes || self._nodes;
1017 isMipmap = isMipmap || false;
1019 node.image = node.image || new Image();
1021 $( node.image ).one( "load", function ( e ) {
1022 self._bindTexture( gl, node, this, isMipmap );
1023 node.imageID = imageID;
1025 if ( !self._animationID ) {
1026 self._setPosition( 0, 0 );
1030 if ( self.options.thumbnailCache ) {
1031 $.imageloader.getThumbnail( url, function ( result ) {
1032 if ( result === "NOT_FOUND_ERR" ) {
1033 $.imageloader.setThumbnail( url, function ( result ) {
1034 if ( result && result.length > 30 ) {
1035 node.image.src = result;
1038 node.image.src = url;
1041 } else if ( result && result.length > 30 ) {
1042 node.image.src = result;
1045 node.image.src = url;
1049 node.image.src = url;
1053 _bindTexture: function ( gl, node, image, isMipmap ) {
1054 if ( !node || !node.texture ) {
1058 gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
1060 gl.bindTexture( gl.TEXTURE_2D, node.texture );
1061 gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image );
1064 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1065 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST );
1066 gl.generateMipmap( gl.TEXTURE_2D );
1068 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
1069 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
1072 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
1073 gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
1075 node.texture.loaded = true;
1077 // release texture memory
1078 gl.bindTexture( gl.TEXTURE_2D, null );
1081 // ----------------------------------------------------------
1083 // ----------------------------------------------------------
1084 _setPosition: function ( progress, direction ) {
1086 mat4 = glMatrix.mat4,
1087 nodes = self._nodes,
1088 imageList = self._imageList,
1089 imageListLength = imageList.length,
1090 itemCount = self._MAX_ITEM_COUNT,
1091 displayLength = ( imageListLength > itemCount ) ? itemCount : imageListLength,
1103 nextLevelLenth = ( direction >= 0 ) ? displayLength + 1 : displayLength;
1105 if ( !nodes[i].level ) {
1106 nodes[i].level = displayLength;
1109 for ( i = 0; i < displayLength; i += 1 ) {
1110 if ( !nodes[i].mvMatrix ) {
1111 nodes[i].mvMatrix = mat4.create();
1114 if ( direction > 0 && nodes[i].level >= displayLength ) {
1118 current = path.levels[nodes[i].level];
1119 nextLevel = ( nodes[i].level + nextLevelLenth + direction ) % nextLevelLenth;
1120 next = path.levels[nextLevel];
1122 if ( imageListLength > itemCount ) {
1123 if ( direction > 0 && nextLevel === 1
1124 && self._firstImageNumber !== nodes[i].imageID ) {
1125 self._loadImage( imageList[self._firstImageNumber].src, i, self._firstImageNumber );
1126 } else if ( direction < 0 && nextLevel === nextLevelLenth - 1
1127 && self._lastImageNumber !== nodes[i].imageID ) {
1128 self._loadImage( imageList[self._lastImageNumber].src, i, self._lastImageNumber );
1132 mat4.identity( nodes[i].mvMatrix );
1133 mat4.translate( nodes[i].mvMatrix, [-2.0, -2.0, 1.0] );
1134 mat4.rotate( nodes[i].mvMatrix, degreeToRadian( 19 ), [1, 0, 0] );
1136 t = ( current + ( next - current ) * ( ( progress > 1 ) ? 1 : progress ) );
1138 if ( progress >= self._ANIMATION_END ) {
1139 nodes[i].level = nextLevel || displayLength;
1140 t = path.levels[nodes[i].level];
1143 if ( ( progress < self._ANIMATION_END )
1144 && ( direction <= 0 && nodes[i].level < 1 ) ) {
1145 nodes[i].drawable = false;
1147 nodes[i].drawable = true;
1150 if ( progress === self._ANIMATION_END && nodes[i].level === 1 ) {
1151 self.element.trigger( "select", imageList[ nodes[i].imageID ], nodes[i].imageID );
1154 position = path.getPosition( t );
1155 angle = path.getAngle( t );
1157 mat4.translate( nodes[i].mvMatrix, position );
1158 mat4.rotate( nodes[i].mvMatrix, angle, [0, 1, 0] );
1161 if ( imageListLength > itemCount && progress >= self._ANIMATION_END ) {
1162 self._firstImageNumber = ( self._firstImageNumber - direction ) % imageListLength;
1163 if ( self._firstImageNumber < 0 ) {
1164 self._firstImageNumber = imageListLength - 1;
1167 self._lastImageNumber = ( self._lastImageNumber - direction ) % imageListLength;
1168 if ( self._lastImageNumber < 0 ) {
1169 self._lastImageNumber = imageListLength - 1;
1175 _drawScene: function () {
1176 if ( !this._gl || !this._shaderProgram ) {
1182 shaderProgram = self._shaderProgram,
1183 nodes = self._nodes,
1184 nodesLength = nodes.length,
1187 gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
1189 gl.bindBuffer( gl.ARRAY_BUFFER, self._positionBuffer );
1190 gl.vertexAttribPointer( shaderProgram.vertexPositionAttr, self._positionBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1192 gl.bindBuffer( gl.ARRAY_BUFFER, self._textureCoordBuffer );
1193 gl.vertexAttribPointer( shaderProgram.textureCoordAttr, self._textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1195 gl.bindBuffer( gl.ARRAY_BUFFER, self._normalVectorBuffer );
1196 gl.vertexAttribPointer( shaderProgram.vertexNormalAttr, self._normalVectorBuffer.itemSize, gl.FLOAT, false, 0, 0 );
1198 for ( i = 0; i < nodesLength; i += 1 ) {
1199 if ( nodes[i].drawable ) {
1200 self._drawElement( self._pMatrix, nodes[i] );
1205 _drawElement: function ( perspectiveMatrix, targetNode ) {
1208 vec3 = glMatrix.vec3,
1209 mat3 = glMatrix.mat3,
1210 mat4 = glMatrix.mat4,
1211 shaderProgram = self._shaderProgram,
1212 moveMatrix = targetNode.mvMatrix,
1213 texture = targetNode.texture,
1214 meshIndexBuffer = targetNode.textureBuffer,
1215 meshIndexBufferItemSize = targetNode.textureBufferItemSize,
1216 lightPositions = self._lightsPositionStack,
1220 if ( !moveMatrix ) {
1224 gl.activeTexture( gl.TEXTURE0 );
1225 if ( texture && texture.loaded ) {
1226 gl.bindTexture( gl.TEXTURE_2D, texture );
1228 gl.uniform1i( shaderProgram.sampleUniform, 0 );
1230 LightDir = vec3.create();
1231 vec3.normalize( lightPositions[0], LightDir );
1232 vec3.scale( LightDir, -8 );
1233 gl.uniform3fv( shaderProgram.lightDirU_first, LightDir );
1235 vec3.normalize( lightPositions[1], LightDir );
1236 vec3.scale( LightDir, -1 );
1237 gl.uniform3fv( shaderProgram.lightDirU_second, LightDir );
1238 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, meshIndexBuffer );
1240 gl.uniformMatrix4fv( shaderProgram.perspectiveMU, false, perspectiveMatrix );
1241 gl.uniformMatrix4fv( shaderProgram.transformMU, false, moveMatrix );
1243 normalMatrix = mat3.create();
1244 mat4.toInverseMat3( moveMatrix, normalMatrix );
1245 mat3.transpose( normalMatrix );
1246 gl.uniformMatrix3fv( shaderProgram.normalMU, false, normalMatrix );
1248 gl.drawElements( gl.TRIANGLES, meshIndexBufferItemSize, gl.UNSIGNED_SHORT, 0 );
1250 // release buffer memory
1251 gl.bindBuffer( gl.ARRAY_BUFFER, null );
1252 gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
1254 // release texture memory
1255 gl.bindTexture( gl.TEXTURE_2D, null );
1258 // ----------------------------------------------------------
1260 // ----------------------------------------------------------
1261 _animate: function ( easingType, duration, direction, repeatCount, startValue, _removeCount ) {
1267 easingType = easingType || "linear";
1268 startValue = startValue || 0;
1269 _removeCount = _removeCount || 0;
1271 if ( self._sumTime >= duration ) {
1272 self._setPosition( self._ANIMATION_END, direction );
1277 if ( self._startTime === 0 ) {
1278 self._startTime = timeNow;
1280 self._sumTime = timeNow - self._startTime;
1281 progress = $.easing[ easingType ]( self._sumTime / duration, self._sumTime, startValue, repeatCount + 1, duration );
1282 removeCount = parseInt( Math.abs( progress ), 10 );
1284 if ( _removeCount !== removeCount ) {
1285 self._setPosition( self._ANIMATION_END, direction );
1286 _removeCount = removeCount;
1288 if ( ( repeatCount - _removeCount ) >= 0 ) {
1289 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1296 self._setPosition( progress - _removeCount, direction );
1299 self._animationID = requestAnimationFrame( function () {
1300 self._animate( easingType, duration, direction, repeatCount, startValue, _removeCount );
1304 _run: function ( direction, repeatCount, startValue ) {
1306 repeat = repeatCount || 0,
1307 duration = self._DURATION_DEFAULT * ( repeat + 1 );
1309 if ( self._imageList.length <= 1 ) {
1313 startValue = startValue || 0;
1314 duration = ( duration >= 0 ) ? duration : 0;
1316 if ( self._animationID ) {
1317 self._setPosition( self._ANIMATION_END, direction );
1321 self._animate( "easeOutExpo", duration, direction, repeat, startValue );
1324 _reset: function () {
1325 if ( !this._canvas || !this._gl ) {
1334 _stop: function () {
1335 if ( this._animationID ) {
1336 cancelAnimationFrame( this._animationID );
1338 this._animationID = 0;
1340 this._startTime = 0;
1345 this._run( this._DIRECTION_LEFT , 0 );
1349 this._run( this._DIRECTION_RIGHT, 0 );
1352 refresh: function () {
1353 var view = this.element,
1354 canvas = view.find( "canvas.ui-gallery3d-canvas" );
1356 if ( canvas.width() !== view.width() ) {
1357 canvas.width( view.width() );
1360 if ( !this._animationID ) {
1361 this._setPosition( 0, 0 );
1365 select: function ( index ) {
1366 var nodes = this._nodes,
1374 if ( index && this._animationID ) {
1378 for ( i in nodes ) {
1379 if ( nodes[i].level === 1 ) {
1380 object = this._imageList[ nodes[i].imageID ];
1381 imageID = nodes[i].imageID;
1390 if ( index < 0 && index >= this._imageList.length ) {
1394 target = index - imageID;
1395 direction = ( target > 0 ) ? this._DIRECTION_LEFT
1396 : ( ( target < 0 ) ? this._DIRECTION_RIGHT : 0 );
1398 this._run( direction, Math.abs( target ) - 1 );
1402 add: function ( item, index ) {
1407 if ( typeof item === "string" ) {
1408 item = { "src" : item };
1412 if ( typeof index !== "number" && index < 0
1413 && index >= this._imageList.length ) {
1417 this._imageList.splice( index, 0, item );
1423 remove: function ( index ) {
1425 if ( typeof index !== "number" && index < 0
1426 && index >= this._imageList.length ) {
1430 this._imageList.splice( index, 1 );
1436 clearThumbnailCache: function () {
1437 if ( !this._nodes || ( this._nodes.length <= 0 ) ) {
1442 for ( i = 0; i < this._imageList.length; i += 1 ) {
1443 url = this._imageList[i].src;
1444 $.imageloader.removeThumbnail( url );
1448 empty: function () {
1449 this._imageList = [];
1453 length: function () {
1454 return this._imageList.length;
1458 $( document ).on( "pagecreate create", function ( e ) {
1459 $( ":jqmData(role='gallery3d')" ).gallery3d();
1460 }).on( "pagechange", function ( e ) {
1461 $( e.target ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1464 $( window ).on( "resize orientationchange", function ( e ) {
1465 $( ".ui-page-active" ).find( ".ui-gallery3d" ).gallery3d( "refresh" );
1468 } ( jQuery, document, window ) );