Mercurial > ~darius > hgwebdir.cgi > iwws
comparison static/jqtouch.js @ 0:2d9ee2b3ae82
Initial commit of iWWS.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Mon, 15 Aug 2011 17:44:56 +0930 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:2d9ee2b3ae82 |
---|---|
1 /* | |
2 | |
3 _/ _/_/ _/_/_/_/_/ _/ | |
4 _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ | |
5 _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ | |
6 _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ | |
7 _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ | |
8 _/ | |
9 _/ | |
10 | |
11 Created by David Kaneda <http://www.davidkaneda.com> | |
12 Documentation and issue tracking on GitHub <http://wiki.github.com/senchalabs/jQTouch/> | |
13 | |
14 Special thanks to Jonathan Stark <http://jonathanstark.com/> | |
15 and pinch/zoom <http://www.pinchzoom.com/> | |
16 | |
17 (c) 2010 by jQTouch project members. | |
18 See LICENSE.txt for license. | |
19 | |
20 $Revision: 166 $ | |
21 $Date: Tue Mar 29 01:24:46 EDT 2011 $ | |
22 $LastChangedBy: jonathanstark $ | |
23 | |
24 | |
25 */ | |
26 | |
27 (function($) { | |
28 $.jQTouch = function(options) { | |
29 | |
30 // Initialize internal jQT variables | |
31 var $body, | |
32 $head=$('head'), | |
33 initialPageId='', | |
34 hist=[], | |
35 newPageCount=0, | |
36 jQTSettings={}, | |
37 currentPage='', | |
38 orientation='portrait', | |
39 tapReady=true, | |
40 lastTime=0, | |
41 lastAnimationTime=0, | |
42 touchSelectors=[], | |
43 publicObj={}, | |
44 tapBuffer=351, | |
45 extensions=$.jQTouch.prototype.extensions, | |
46 animations=[], | |
47 hairExtensions='', | |
48 defaults = { | |
49 addGlossToIcon: true, | |
50 backSelector: '.back, .cancel, .goback', | |
51 cacheGetRequests: true, | |
52 debug: false, | |
53 fallback2dAnimation: 'fade', | |
54 fixedViewport: true, | |
55 formSelector: 'form', | |
56 fullScreen: true, | |
57 fullScreenClass: 'fullscreen', | |
58 hoverDelay: 50, | |
59 icon: null, | |
60 iconPad: null, // available in iOS 4.2 and later. | |
61 icon4: null, // available in iOS 4.2 and later. | |
62 moveThreshold: 10, | |
63 preloadImages: false, | |
64 pressDelay: 1000, | |
65 startupScreen: null, | |
66 statusBar: 'default', // other options: black-translucent, black | |
67 submitSelector: '.submit', | |
68 touchSelector: 'a, .touch', | |
69 useAnimations: true, | |
70 useFastTouch: true, // experimental | |
71 animations: [ // highest to lowest priority | |
72 {selector:'.cube', name:'cubeleft', is3d:true}, | |
73 {selector:'.cubeleft', name:'cubeleft', is3d:true}, | |
74 {selector:'.cuberight', name:'cuberight', is3d:true}, | |
75 {selector:'.dissolve', name:'dissolve', is3d:false}, | |
76 {selector:'.fade', name:'fade', is3d:false}, | |
77 {selector:'.flip', name:'flipleft', is3d:true}, | |
78 {selector:'.flipleft', name:'flipleft', is3d:true}, | |
79 {selector:'.flipright', name:'flipright', is3d:true}, | |
80 {selector:'.pop', name:'pop', is3d:true}, | |
81 {selector:'.slide', name:'slideleft', is3d:false}, | |
82 {selector:'.slidedown', name:'slidedown', is3d:false}, | |
83 {selector:'.slideleft', name:'slideleft', is3d:false}, | |
84 {selector:'.slideright', name:'slideright', is3d:false}, | |
85 {selector:'.slideup', name:'slideup', is3d:false}, | |
86 {selector:'.swap', name:'swapleft', is3d:true}, | |
87 {selector:'#jqt > * > ul li a', name:'slideleft', is3d:false} | |
88 ] | |
89 }; | |
90 | |
91 function _debug(message) { | |
92 var now = (new Date).getTime(); | |
93 var delta = now - lastTime; | |
94 lastTime = now; | |
95 if (jQTSettings.debug) { | |
96 if (message) { | |
97 _log(delta + ': ' + message); | |
98 } else { | |
99 _log(delta + ': ' + 'Called ' + arguments.callee.caller.name); | |
100 } | |
101 } | |
102 } | |
103 function _log(message) { | |
104 if (window.console !== undefined) { | |
105 console.log(message); | |
106 } | |
107 } | |
108 function addAnimation(animation) { | |
109 // _debug(); | |
110 if (typeof(animation.selector) === 'string' && typeof(animation.name) === 'string') { | |
111 animations.push(animation); | |
112 } | |
113 } | |
114 function addPageToHistory(page, animation) { | |
115 _debug(); | |
116 hist.unshift({ | |
117 page: page, | |
118 animation: animation, | |
119 hash: '#' + page.attr('id'), | |
120 id: page.attr('id') | |
121 }); | |
122 } | |
123 function clickHandler(e) { | |
124 _debug(); | |
125 | |
126 if (!tapReady) { | |
127 _debug('ClickHandler handler aborted because tap is not ready'); | |
128 e.preventDefault(); | |
129 return false; | |
130 } | |
131 | |
132 // Figure out whether to prevent default | |
133 var $el = $(e.target); | |
134 | |
135 // Find the nearest tappable ancestor | |
136 if (!$el.is(touchSelectors.join(', '))) { | |
137 $el = $(e.target).closest(touchSelectors.join(', ')); | |
138 } | |
139 | |
140 // Prevent default if we found an internal link (relative or absolute) | |
141 if ($el && $el.attr('href') && !$el.isExternalLink()) { | |
142 _debug('Need to prevent default click behavior'); | |
143 e.preventDefault(); | |
144 } else { | |
145 _debug('No need to prevent default click behavior'); | |
146 } | |
147 | |
148 // Trigger a tap event if touchstart is not on the job | |
149 if ($.support.touch) { | |
150 _debug('Not converting click to a tap event because touch handler is on the job'); | |
151 } else { | |
152 _debug('Converting click event to a tap event because touch handlers are not present or off'); | |
153 $(e.target).trigger('tap', e); | |
154 } | |
155 | |
156 } | |
157 function doNavigation(fromPage, toPage, animation, goingBack) { | |
158 _debug(); | |
159 | |
160 // Error check for target page | |
161 if (toPage.length === 0) { | |
162 $.fn.unselect(); | |
163 _debug('Target element is missing.'); | |
164 return false; | |
165 } | |
166 | |
167 // Error check for fromPage===toPage | |
168 if (toPage.hasClass('current')) { | |
169 $.fn.unselect(); | |
170 _debug('You are already on the page you are trying to navigate to.'); | |
171 return false; | |
172 } | |
173 | |
174 // Collapse the keyboard | |
175 $(':focus').blur(); | |
176 | |
177 // Position the incoming page so toolbar is at top of viewport regardless of scroll position on from page | |
178 // toPage.css('top', window.pageYOffset); | |
179 | |
180 fromPage.trigger('pageAnimationStart', { direction: 'out' }); | |
181 toPage.trigger('pageAnimationStart', { direction: 'in' }); | |
182 | |
183 if ($.support.animationEvents && animation && jQTSettings.useAnimations) { | |
184 | |
185 tapReady = false; | |
186 | |
187 // Fail over to 2d animation if need be | |
188 if (!$.support.transform3d && animation.is3d) { | |
189 animation.name = jQTSettings.fallback2dAnimation; | |
190 } | |
191 | |
192 // Reverse animation if need be | |
193 var finalAnimationName; | |
194 if (goingBack) { | |
195 if (animation.name.indexOf('left') > 0) { | |
196 finalAnimationName = animation.name.replace(/left/, 'right'); | |
197 } else if (animation.name.indexOf('right') > 0) { | |
198 finalAnimationName = animation.name.replace(/right/, 'left'); | |
199 } else if (animation.name.indexOf('up') > 0) { | |
200 finalAnimationName = animation.name.replace(/up/, 'down'); | |
201 } else if (animation.name.indexOf('down') > 0) { | |
202 finalAnimationName = animation.name.replace(/down/, 'up'); | |
203 } else { | |
204 finalAnimationName = animation.name; | |
205 } | |
206 } else { | |
207 finalAnimationName = animation.name; | |
208 } | |
209 | |
210 // _debug('finalAnimationName is ' + finalAnimationName); | |
211 | |
212 // Bind internal "cleanup" callback | |
213 fromPage.bind('webkitAnimationEnd', navigationEndHandler); | |
214 fromPage.bind('webkitTransitionEnd', navigationEndHandler); | |
215 | |
216 // Trigger animations | |
217 scrollTo(0, 0); | |
218 toPage.addClass(finalAnimationName + ' in current'); | |
219 fromPage.addClass(finalAnimationName + ' out'); | |
220 | |
221 } else { | |
222 toPage.addClass('current'); | |
223 navigationEndHandler(); | |
224 } | |
225 | |
226 // Define private navigationEnd callback | |
227 function navigationEndHandler(event) { | |
228 _debug(); | |
229 | |
230 if ($.support.animationEvents && animation && jQTSettings.useAnimations) { | |
231 fromPage.unbind('webkitAnimationEnd', navigationEndHandler); | |
232 fromPage.unbind('webkitTransitionEnd', navigationEndHandler); | |
233 fromPage.removeClass(finalAnimationName + ' out current'); | |
234 toPage.removeClass(finalAnimationName + ' in'); | |
235 // scrollTo(0, 0); | |
236 // toPage.css('top', 0); | |
237 } else { | |
238 fromPage.removeClass(finalAnimationName + ' out current'); | |
239 } | |
240 | |
241 // Housekeeping | |
242 currentPage = toPage; | |
243 if (goingBack) { | |
244 hist.shift(); | |
245 } else { | |
246 addPageToHistory(currentPage, animation); | |
247 } | |
248 | |
249 fromPage.unselect(); | |
250 lastAnimationTime = (new Date()).getTime(); | |
251 setHash(currentPage.attr('id')); | |
252 tapReady = true; | |
253 | |
254 // Trigger custom events | |
255 toPage.trigger('pageAnimationEnd', {direction:'in', animation:animation}); | |
256 fromPage.trigger('pageAnimationEnd', {direction:'out', animation:animation}); | |
257 | |
258 } | |
259 | |
260 // We's out | |
261 return true; | |
262 } | |
263 function getOrientation() { | |
264 _debug(); | |
265 return orientation; | |
266 } | |
267 function goBack() { | |
268 _debug(); | |
269 | |
270 // Error checking | |
271 if (hist.length < 1 ) { | |
272 _debug('History is empty.'); | |
273 } | |
274 | |
275 if (hist.length === 1 ) { | |
276 _debug('You are on the first panel.'); | |
277 } | |
278 | |
279 var from = hist[0], to = hist[1]; | |
280 | |
281 if (doNavigation(from.page, to.page, from.animation, true)) { | |
282 return publicObj; | |
283 } else { | |
284 _debug('Could not go back.'); | |
285 return false; | |
286 } | |
287 | |
288 } | |
289 function goTo(toPage, animation, reverse) { | |
290 _debug(); | |
291 | |
292 if (reverse) { | |
293 _log('The reverse parameter of the goTo() function has been deprecated.'); | |
294 } | |
295 | |
296 var fromPage = hist[0].page; | |
297 | |
298 if (typeof animation === 'string') { | |
299 for (var i=0, max=animations.length; i < max; i++) { | |
300 if (animations[i].name === animation) { | |
301 animation = animations[i]; | |
302 break; | |
303 } | |
304 } | |
305 } | |
306 | |
307 if (typeof(toPage) === 'string') { | |
308 var nextPage = $(toPage); | |
309 if (nextPage.length < 1) { | |
310 showPageByHref(toPage, { | |
311 'animation': animation | |
312 }); | |
313 return; | |
314 } else { | |
315 toPage = nextPage; | |
316 } | |
317 | |
318 } | |
319 if (doNavigation(fromPage, toPage, animation)) { | |
320 return publicObj; | |
321 } else { | |
322 _debug('Could not animate pages.'); | |
323 return false; | |
324 } | |
325 } | |
326 function hashChangeHandler(e) { | |
327 _debug(); | |
328 if (location.hash === hist[0].hash) { | |
329 _debug('We are on the right panel'); | |
330 } else { | |
331 _debug('We are not on the right panel'); | |
332 if(location.hash === hist[1].hash) { | |
333 goBack(); | |
334 } else { | |
335 _debug(location.hash + ' !== ' + hist[1].hash); | |
336 } | |
337 } | |
338 } | |
339 function init(options) { | |
340 _debug(); | |
341 jQTSettings = $.extend({}, defaults, options); | |
342 | |
343 // Preload images | |
344 if (jQTSettings.preloadImages) { | |
345 for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--) { | |
346 (new Image()).src = jQTSettings.preloadImages[i]; | |
347 }; | |
348 } | |
349 | |
350 // Set appropriate icon (retina display available in iOS 4.2 and later.) | |
351 var precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed'; | |
352 if (jQTSettings.icon) { | |
353 hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" href="' + jQTSettings.icon + '" />'; | |
354 } | |
355 if (jQTSettings.iconPad) { | |
356 hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" sizes="72x72" href="' + jQTSettings.iconPad + '" />'; | |
357 } | |
358 if (jQTSettings.icon4) { | |
359 hairExtensions += '<link rel="apple-touch-icon' + precomposed + '" sizes="114x114" href="' + jQTSettings.icon4 + '" />'; | |
360 } | |
361 | |
362 // Set startup screen | |
363 if (jQTSettings.startupScreen) { | |
364 hairExtensions += '<link rel="apple-touch-startup-image" href="' + jQTSettings.startupScreen + '" />'; | |
365 } | |
366 | |
367 // Set viewport | |
368 if (jQTSettings.fixedViewport) { | |
369 hairExtensions += '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"/>'; | |
370 } | |
371 | |
372 // Set full-screen | |
373 if (jQTSettings.fullScreen) { | |
374 hairExtensions += '<meta name="apple-mobile-web-app-capable" content="yes" />'; | |
375 if (jQTSettings.statusBar) { | |
376 hairExtensions += '<meta name="apple-mobile-web-app-status-bar-style" content="' + jQTSettings.statusBar + '" />'; | |
377 } | |
378 } | |
379 | |
380 // Attach hair extensions | |
381 if (hairExtensions) { | |
382 $head.prepend(hairExtensions); | |
383 } | |
384 | |
385 } | |
386 function insertPages(nodes, animation) { | |
387 _debug(); | |
388 | |
389 var targetPage = null; | |
390 $(nodes).each(function(index, node) { | |
391 var $node = $(this); | |
392 if (!$node.attr('id')) { | |
393 $node.attr('id', 'page-' + (++newPageCount)); | |
394 } | |
395 | |
396 // Remove any existing instance | |
397 $('#' + $node.attr('id')).remove(); | |
398 | |
399 $body.trigger('pageInserted', {page: $node.appendTo($body)}); | |
400 | |
401 if ($node.hasClass('current') || !targetPage) { | |
402 targetPage = $node; | |
403 } | |
404 }); | |
405 if (targetPage !== null) { | |
406 goTo(targetPage, animation); | |
407 return targetPage; | |
408 } else { | |
409 return false; | |
410 } | |
411 } | |
412 function mousedownHandler(e) { | |
413 var timeDiff = (new Date()).getTime() - lastAnimationTime; | |
414 if (timeDiff < tapBuffer) { | |
415 return false; | |
416 } | |
417 } | |
418 function orientationChangeHandler() { | |
419 _debug(); | |
420 | |
421 orientation = Math.abs(window.orientation) == 90 ? 'landscape' : 'portrait'; | |
422 $body.removeClass('portrait landscape').addClass(orientation).trigger('turn', {orientation: orientation}); | |
423 } | |
424 function setHash(hash) { | |
425 _debug(); | |
426 | |
427 // Trim leading # if need be | |
428 hash = hash.replace(/^#/, ''), | |
429 | |
430 // Change hash | |
431 location.hash = '#' + hash; | |
432 | |
433 } | |
434 function showPageByHref(href, options) { | |
435 _debug(); | |
436 | |
437 var defaults = { | |
438 data: null, | |
439 method: 'GET', | |
440 animation: null, | |
441 callback: null, | |
442 $referrer: null | |
443 }; | |
444 | |
445 var settings = $.extend({}, defaults, options); | |
446 | |
447 if (href != '#') { | |
448 $.ajax({ | |
449 url: href, | |
450 data: settings.data, | |
451 type: settings.method, | |
452 success: function (data, textStatus) { | |
453 var firstPage = insertPages(data, settings.animation); | |
454 if (firstPage) { | |
455 if (settings.method == 'GET' && jQTSettings.cacheGetRequests === true && settings.$referrer) { | |
456 settings.$referrer.attr('href', '#' + firstPage.attr('id')); | |
457 } | |
458 if (settings.callback) { | |
459 settings.callback(true); | |
460 } | |
461 } | |
462 }, | |
463 error: function (data) { | |
464 if (settings.$referrer) { | |
465 settings.$referrer.unselect(); | |
466 } | |
467 if (settings.callback) { | |
468 settings.callback(false); | |
469 } | |
470 } | |
471 }); | |
472 } else if (settings.$referrer) { | |
473 settings.$referrer.unselect(); | |
474 } | |
475 } | |
476 function submitHandler(e, callback) { | |
477 _debug(); | |
478 | |
479 $(':focus').blur(); | |
480 | |
481 e.preventDefault(); | |
482 | |
483 var $form = (typeof(e)==='string') ? $(e).eq(0) : (e.target ? $(e.target) : $(e)); | |
484 | |
485 _debug($form.attr('action')); | |
486 | |
487 if ($form.length && $form.is(jQTSettings.formSelector) && $form.attr('action')) { | |
488 showPageByHref($form.attr('action'), { | |
489 data: $form.serialize(), | |
490 method: $form.attr('method') || "POST", | |
491 animation: animations[0] || null, | |
492 callback: callback | |
493 }); | |
494 return false; | |
495 } | |
496 return false; | |
497 } | |
498 function submitParentForm($el) { | |
499 _debug(); | |
500 | |
501 var $form = $el.closest('form'); | |
502 if ($form.length === 0) { | |
503 _debug('No parent form found'); | |
504 } else { | |
505 _debug('About to submit parent form'); | |
506 var evt = $.Event('submit'); | |
507 evt.preventDefault(); | |
508 $form.trigger(evt); | |
509 return false; | |
510 } | |
511 return true; | |
512 } | |
513 function supportForAnimationEvents() { | |
514 _debug(); | |
515 | |
516 return (typeof WebKitAnimationEvent != 'undefined'); | |
517 } | |
518 function supportForCssMatrix() { | |
519 _debug(); | |
520 | |
521 return (typeof WebKitCSSMatrix != 'undefined'); | |
522 } | |
523 function supportForTouchEvents() { | |
524 _debug(); | |
525 | |
526 /* | |
527 // If dev wants fast touch off, shut off touch whether device supports it or not | |
528 if (!jQTSettings.useFastTouch) { | |
529 return false | |
530 } | |
531 | |
532 */ | |
533 // Dev must want touch, so check for support | |
534 if (typeof TouchEvent != 'undefined') { | |
535 if (window.navigator.userAgent.indexOf('Mobile') > -1) { // Grrrr... | |
536 return true; | |
537 } else { | |
538 return false; | |
539 } | |
540 } else { | |
541 return false; | |
542 } | |
543 }; | |
544 function supportForTransform3d() { | |
545 _debug(); | |
546 | |
547 var head, body, style, div, result; | |
548 | |
549 head = document.getElementsByTagName('head')[0]; | |
550 body = document.body; | |
551 | |
552 style = document.createElement('style'); | |
553 style.textContent = '@media (transform-3d),(-o-transform-3d),(-moz-transform-3d),(-ms-transform-3d),(-webkit-transform-3d),(modernizr){#jqtTestFor3dSupport{height:3px}}'; | |
554 | |
555 div = document.createElement('div'); | |
556 div.id = 'jqtTestFor3dSupport'; | |
557 | |
558 // Add to the page | |
559 head.appendChild(style); | |
560 body.appendChild(div); | |
561 | |
562 // Check the result | |
563 result = div.offsetHeight === 3; | |
564 | |
565 // Clean up | |
566 style.parentNode.removeChild(style); | |
567 div.parentNode.removeChild(div); | |
568 | |
569 // Pass back result | |
570 // _debug('Support for 3d transforms: ' + result); | |
571 return result; | |
572 }; | |
573 function tapHandler(e){ | |
574 _debug(); | |
575 | |
576 if (!tapReady) { | |
577 _debug('Tap is not ready'); | |
578 return false; | |
579 } | |
580 | |
581 // Grab the target element | |
582 var $el = $(e.target); | |
583 | |
584 // Find the nearest tappable ancestor | |
585 if (!$el.is(touchSelectors.join(', '))) { | |
586 var $el = $(e.target).closest(touchSelectors.join(', ')); | |
587 } | |
588 | |
589 // Make sure we have a tappable element | |
590 if (!$el.length || !$el.attr('href')) { | |
591 _debug('Could not find a link related to tapped element'); | |
592 return false; | |
593 } | |
594 | |
595 // Init some vars | |
596 var target = $el.attr('target'), | |
597 hash = $el.attr('hash') || ($el.prop && $el.prop('hash')), //jQuery attr vs. prop | |
598 animation = null; | |
599 | |
600 if ($el.isExternalLink()) { | |
601 $el.unselect(); | |
602 return true; | |
603 | |
604 } else if ($el.is(jQTSettings.backSelector)) { | |
605 // User clicked or tapped a back button | |
606 goBack(hash); | |
607 | |
608 } else if ($el.is(jQTSettings.submitSelector)) { | |
609 // User clicked or tapped a submit element | |
610 submitParentForm($el); | |
611 | |
612 } else if (target === '_webapp') { | |
613 // User clicked or tapped an internal link, fullscreen mode | |
614 window.location = $el.attr('href'); | |
615 return false; | |
616 | |
617 } else if ($el.attr('href') === '#') { | |
618 // Allow tap on item with no href | |
619 $el.unselect(); | |
620 return true; | |
621 | |
622 } else { | |
623 | |
624 // Figure out the animation to use | |
625 for (var i=0, max=animations.length; i < max; i++) { | |
626 if ($el.is(animations[i].selector)) { | |
627 animation = animations[i]; | |
628 break; | |
629 } | |
630 }; | |
631 | |
632 if (!animation) { | |
633 _log('Animation could not be found. Using slideleft.'); | |
634 animation = 'slideleft'; | |
635 } | |
636 | |
637 if (hash && hash !== '#') { | |
638 // Internal href | |
639 $el.addClass('active'); | |
640 goTo($(hash).data('referrer', $el), animation, $el.hasClass('reverse')); | |
641 return false; | |
642 | |
643 } else { | |
644 // External href | |
645 $el.addClass('loading active'); | |
646 showPageByHref($el.attr('href'), { | |
647 animation: animation, | |
648 callback: function() { | |
649 $el.removeClass('loading'); | |
650 setTimeout($.fn.unselect, 250, $el); | |
651 }, | |
652 $referrer: $el | |
653 }); | |
654 return false; | |
655 } | |
656 } | |
657 } | |
658 function touchStartHandler(e) { | |
659 _debug(); | |
660 | |
661 if (!tapReady) { | |
662 _debug('TouchStart handler aborted because tap is not ready'); | |
663 e.preventDefault(); | |
664 return false; | |
665 } | |
666 | |
667 var $el = $(e.target); | |
668 | |
669 // Error check | |
670 if (!$el.length) { | |
671 _debug('Could not find target of touchstart event.'); | |
672 return; | |
673 } | |
674 | |
675 var startTime = (new Date).getTime(), | |
676 hoverTimeout = null, | |
677 pressTimeout = null, | |
678 touch, | |
679 startX, | |
680 startY, | |
681 deltaX = 0, | |
682 deltaY = 0, | |
683 deltaT = 0; | |
684 | |
685 if (event.changedTouches && event.changedTouches.length) { | |
686 touch = event.changedTouches[0]; | |
687 startX = touch.pageX; | |
688 startY = touch.pageY; | |
689 } | |
690 | |
691 // Prep the element | |
692 $el.bind('touchmove',touchMoveHandler).bind('touchend',touchEndHandler).bind('touchcancel',touchCancelHandler); | |
693 | |
694 hoverTimeout = setTimeout(function() { | |
695 $el.makeActive(); | |
696 }, jQTSettings.hoverDelay); | |
697 | |
698 pressTimeout = setTimeout(function() { | |
699 $el.unbind('touchmove',touchMoveHandler).unbind('touchend',touchEndHandler).unbind('touchcancel',touchCancelHandler); | |
700 $el.unselect(); | |
701 clearTimeout(hoverTimeout); | |
702 $el.trigger('press'); | |
703 }, jQTSettings.pressDelay); | |
704 | |
705 // Private touch functions | |
706 function touchCancelHandler(e) { | |
707 _debug(); | |
708 clearTimeout(hoverTimeout); | |
709 $el.unselect(); | |
710 $el.unbind('touchmove',touchMoveHandler).unbind('touchend',touchEndHandler).unbind('touchcancel',touchCancelHandler); | |
711 } | |
712 | |
713 function touchEndHandler(e) { | |
714 _debug(); | |
715 // updateChanges(); | |
716 $el.unbind('touchend',touchEndHandler).unbind('touchcancel',touchCancelHandler); | |
717 clearTimeout(hoverTimeout); | |
718 clearTimeout(pressTimeout); | |
719 if (Math.abs(deltaX) < jQTSettings.moveThreshold && Math.abs(deltaY) < jQTSettings.moveThreshold && deltaT < jQTSettings.pressDelay) { | |
720 // e.preventDefault(); | |
721 // e.stopImmediatePropagation(); | |
722 $el.trigger('tap', e); | |
723 } else { | |
724 $el.unselect(); | |
725 } | |
726 } | |
727 | |
728 function touchMoveHandler(e) { | |
729 // _debug(); | |
730 updateChanges(); | |
731 var absX = Math.abs(deltaX); | |
732 var absY = Math.abs(deltaY); | |
733 var direction; | |
734 if (absX > absY && (absX > 35) && deltaT < 1000) { | |
735 if (deltaX < 0) { | |
736 direction = 'left'; | |
737 } else { | |
738 direction = 'right'; | |
739 } | |
740 $el.unbind('touchmove',touchMoveHandler).unbind('touchend',touchEndHandler).unbind('touchcancel',touchCancelHandler); | |
741 $el.trigger('swipe', {direction:direction, deltaX:deltaX, deltaY: deltaY}); | |
742 } | |
743 $el.unselect(); | |
744 clearTimeout(hoverTimeout); | |
745 if (absX > jQTSettings.moveThreshold || absY > jQTSettings.moveThreshold) { | |
746 clearTimeout(pressTimeout); | |
747 } | |
748 } | |
749 | |
750 function updateChanges() { | |
751 // _debug(); | |
752 var firstFinger = event.changedTouches[0] || null; | |
753 deltaX = firstFinger.pageX - startX; | |
754 deltaY = firstFinger.pageY - startY; | |
755 deltaT = (new Date).getTime() - startTime; | |
756 // _debug('deltaX:'+deltaX+';deltaY:'+deltaY+';'); | |
757 } | |
758 | |
759 } // End touch handler | |
760 function useFastTouch(setting) { | |
761 _debug(); | |
762 | |
763 if (setting !== undefined) { | |
764 if (setting === true) { | |
765 if (supportForTouchEvents()) { | |
766 $.support.touch = true; | |
767 } else{ | |
768 _log('This device does not support touch events'); | |
769 }; | |
770 } else { | |
771 $.support.touch = false; | |
772 } | |
773 } | |
774 | |
775 return $.support.touch; | |
776 | |
777 } | |
778 | |
779 // Get the party started | |
780 init(options); | |
781 | |
782 // Document ready stuff | |
783 $(document).ready(function() { | |
784 | |
785 // Store some properties in the jQuery support object | |
786 $.support.animationEvents = supportForAnimationEvents(); | |
787 $.support.cssMatrix = supportForCssMatrix(); | |
788 $.support.touch = supportForTouchEvents() && jQTSettings.useFastTouch; | |
789 $.support.transform3d = supportForTransform3d(); | |
790 | |
791 if (!$.support.touch) { | |
792 _log('This device does not support touch interaction, or it has been deactivated by the developer. Some features might be unavailable.'); | |
793 } | |
794 if (!$.support.transform3d) { | |
795 _log('This device does not support 3d animation. 2d animations will be used instead.'); | |
796 } | |
797 | |
798 // Define public jQuery functions | |
799 $.fn.isExternalLink = function() { | |
800 var $el = $(this); | |
801 return ($el.attr('target') == '_blank' || $el.attr('rel') == 'external' || $el.is('a[href^="http://maps.google.com"], a[href^="mailto:"], a[href^="tel:"], a[href^="javascript:"], a[href*="youtube.com/v"], a[href*="youtube.com/watch"]')); | |
802 } | |
803 $.fn.makeActive = function() { | |
804 return $(this).addClass('active'); | |
805 } | |
806 $.fn.press = function(fn) { | |
807 if ($.isFunction(fn)) { | |
808 return $(this).live('press', fn); | |
809 } else { | |
810 return $(this).trigger('press'); | |
811 } | |
812 } | |
813 $.fn.swipe = function(fn) { | |
814 if ($.isFunction(fn)) { | |
815 return $(this).live('swipe', fn); | |
816 } else { | |
817 return $(this).trigger('swipe'); | |
818 } | |
819 } | |
820 $.fn.tap = function(fn) { | |
821 if ($.isFunction(fn)) { | |
822 return $(this).live('tap', fn); | |
823 } else { | |
824 return $(this).trigger('tap'); | |
825 } | |
826 } | |
827 $.fn.unselect = function(obj) { | |
828 if (obj) { | |
829 obj.removeClass('active'); | |
830 } else { | |
831 $('.active').removeClass('active'); | |
832 } | |
833 } | |
834 | |
835 // Add extensions | |
836 for (var i=0, max=extensions.length; i < max; i++) { | |
837 var fn = extensions[i]; | |
838 if ($.isFunction(fn)) { | |
839 $.extend(publicObj, fn(publicObj)); | |
840 } | |
841 } | |
842 | |
843 // Set up animations array | |
844 if (jQTSettings['cubeSelector']) { | |
845 _log('NOTE: cubeSelector has been deprecated. Please use cubeleftSelector instead.'); | |
846 jQTSettings['cubeleftSelector'] = jQTSettings['cubeSelector']; | |
847 } | |
848 if (jQTSettings['flipSelector']) { | |
849 _log('NOTE: flipSelector has been deprecated. Please use flipleftSelector instead.'); | |
850 jQTSettings['flipleftSelector'] = jQTSettings['flipSelector']; | |
851 } | |
852 if (jQTSettings['slideSelector']) { | |
853 _log('NOTE: slideSelector has been deprecated. Please use slideleftSelector instead.'); | |
854 jQTSettings['slideleftSelector'] = jQTSettings['slideSelector']; | |
855 } | |
856 for (var i=0, max=defaults.animations.length; i < max; i++) { | |
857 var animation = defaults.animations[i]; | |
858 if(jQTSettings[animation.name + 'Selector'] !== undefined){ | |
859 animation.selector = jQTSettings[animation.name + 'Selector']; | |
860 } | |
861 addAnimation(animation); | |
862 } | |
863 | |
864 // Create an array of stuff that needs touch event handling | |
865 touchSelectors.push('input'); // TODO: Ask DK why inputs are considered touch selectors | |
866 touchSelectors.push(jQTSettings.touchSelector); | |
867 touchSelectors.push(jQTSettings.backSelector); | |
868 touchSelectors.push(jQTSettings.submitSelector); | |
869 $(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none'); | |
870 | |
871 // Make sure we have a jqt element | |
872 $body = $('#jqt'); | |
873 if ($body.length === 0) { | |
874 _log('Could not find an element with the id "jqt", so the body id has been set to "jqt". If you are having any problems, wrapping your panels in a div with the id "jqt" might help.'); | |
875 $body = $('body').attr('id', 'jqt'); | |
876 } | |
877 | |
878 // Add some specific css if need be | |
879 if ($.support.transform3d) { | |
880 $body.addClass('supports3d'); | |
881 } | |
882 if (jQTSettings.fullScreenClass && window.navigator.standalone == true) { | |
883 $body.addClass(jQTSettings.fullScreenClass + ' ' + jQTSettings.statusBar); | |
884 } | |
885 if (window.navigator.userAgent.match(/Android/ig)) { // Grr... added to postion checkbox labels. Lame. I know. - js | |
886 $body.addClass('android'); | |
887 } | |
888 | |
889 // Bind events | |
890 $(window).bind('hashchange', hashChangeHandler); | |
891 $body.bind('touchstart', touchStartHandler) | |
892 .bind('click', clickHandler) | |
893 .bind('mousedown', mousedownHandler) | |
894 .bind('orientationchange', orientationChangeHandler) | |
895 .bind('submit', submitHandler) | |
896 .bind('tap', tapHandler) | |
897 .trigger('orientationchange'); | |
898 | |
899 | |
900 // Determine what the "current" (initial) panel should be | |
901 if ($('#jqt > .current').length == 0) { | |
902 currentPage = $('#jqt > *:first'); | |
903 } else { | |
904 currentPage = $('#jqt > .current:first'); | |
905 $('#jqt > .current').removeClass('current'); | |
906 } | |
907 | |
908 // Go to the top of the "current" page | |
909 $(currentPage).addClass('current'); | |
910 initialPageId = $(currentPage).attr('id'); | |
911 setHash(initialPageId); | |
912 addPageToHistory(currentPage); | |
913 scrollTo(0, 0); | |
914 | |
915 // Make sure none of the panels yank the location bar into view | |
916 $('#jqt > *').css('minHeight', window.innerHeight); | |
917 | |
918 }); | |
919 | |
920 // Expose public methods and properties | |
921 publicObj = { | |
922 addAnimation: addAnimation, | |
923 animations: animations, | |
924 getOrientation: getOrientation, | |
925 goBack: goBack, | |
926 goTo: goTo, | |
927 hist: hist, | |
928 settings: jQTSettings, | |
929 submitForm: submitHandler, | |
930 support: $.support, | |
931 useFastTouch: useFastTouch | |
932 } | |
933 return publicObj; | |
934 } | |
935 | |
936 // Extensions directly manipulate the jQTouch object, before it's initialized. | |
937 $.jQTouch.prototype.extensions = []; | |
938 $.jQTouch.addExtension = function(extension) { | |
939 $.jQTouch.prototype.extensions.push(extension); | |
940 } | |
941 | |
942 })(jQuery); |