[9188] | 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 Google Code <http://code.google.com/p/jqtouch/> |
---|
| 13 | |
---|
| 14 | Special thanks to Jonathan Stark <http://jonathanstark.com/> |
---|
| 15 | and pinch/zoom <http://www.pinchzoom.com/> |
---|
| 16 | |
---|
| 17 | (c) 2009 by jQTouch project members. |
---|
| 18 | See LICENSE.txt for license. |
---|
| 19 | |
---|
| 20 | $Revision: 109 $ |
---|
| 21 | $Date: 2009-10-06 12:23:30 -0400 (Tue, 06 Oct 2009) $ |
---|
| 22 | $LastChangedBy: davidcolbykaneda $ |
---|
| 23 | |
---|
| 24 | */ |
---|
| 25 | |
---|
| 26 | (function($) { |
---|
| 27 | $.jQTouch = function(options) { |
---|
| 28 | |
---|
| 29 | // Set support values |
---|
| 30 | $.support.WebKitCSSMatrix = (typeof WebKitCSSMatrix == "object"); |
---|
| 31 | $.support.touch = (typeof Touch == "object"); |
---|
| 32 | $.support.WebKitAnimationEvent = (typeof WebKitTransitionEvent == "object"); |
---|
| 33 | |
---|
| 34 | // Initialize internal variables |
---|
| 35 | var $body, |
---|
| 36 | $head=$('head'), |
---|
| 37 | hist=[], |
---|
| 38 | newPageCount=0, |
---|
| 39 | jQTSettings={}, |
---|
| 40 | hashCheck, |
---|
| 41 | currentPage, |
---|
| 42 | orientation, |
---|
| 43 | isMobileWebKit = RegExp(" Mobile/").test(navigator.userAgent), |
---|
| 44 | tapReady=true, |
---|
| 45 | lastAnimationTime=0, |
---|
| 46 | touchSelectors=[], |
---|
| 47 | publicObj={}, |
---|
| 48 | extensions=$.jQTouch.prototype.extensions, |
---|
| 49 | defaultAnimations=['slide','flip','slideup','swap','cube','pop','dissolve','fade','back'], |
---|
| 50 | animations=[], |
---|
| 51 | hairextensions=''; |
---|
| 52 | |
---|
| 53 | // Get the party started |
---|
| 54 | init(options); |
---|
| 55 | |
---|
| 56 | function init(options) { |
---|
| 57 | |
---|
| 58 | var defaults = { |
---|
| 59 | addGlossToIcon: true, |
---|
| 60 | backSelector: '.back, .cancel, .goback', |
---|
| 61 | cacheGetRequests: true, |
---|
| 62 | cubeSelector: '.cube', |
---|
| 63 | dissolveSelector: '.dissolve', |
---|
| 64 | fadeSelector: '.fade', |
---|
| 65 | fixedViewport: true, |
---|
| 66 | flipSelector: '.flip', |
---|
| 67 | formSelector: 'form', |
---|
| 68 | fullScreen: true, |
---|
| 69 | fullScreenClass: 'fullscreen', |
---|
| 70 | icon: null, |
---|
| 71 | touchSelector: 'a, .touch', |
---|
| 72 | popSelector: '.pop', |
---|
| 73 | preloadImages: false, |
---|
| 74 | slideSelector: 'body > * > ul li a', |
---|
| 75 | slideupSelector: '.slideup', |
---|
| 76 | startupScreen: null, |
---|
| 77 | statusBar: 'default', // other options: black-translucent, black |
---|
| 78 | submitSelector: '.submit', |
---|
| 79 | swapSelector: '.swap', |
---|
| 80 | useAnimations: true, |
---|
| 81 | useFastTouch: true // Experimental. |
---|
| 82 | }; |
---|
| 83 | jQTSettings = $.extend({}, defaults, options); |
---|
| 84 | |
---|
| 85 | // Preload images |
---|
| 86 | if (jQTSettings.preloadImages) { |
---|
| 87 | for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--){ |
---|
| 88 | (new Image()).src = jQTSettings.preloadImages[i]; |
---|
| 89 | }; |
---|
| 90 | } |
---|
| 91 | // Set icon |
---|
| 92 | if (jQTSettings.icon) { |
---|
| 93 | var precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed'; |
---|
| 94 | hairextensions += '<link rel="apple-touch-icon' + precomposed + '" href="' + jQTSettings.icon + '" />'; |
---|
| 95 | } |
---|
| 96 | // Set startup screen |
---|
| 97 | if (jQTSettings.startupScreen) { |
---|
| 98 | hairextensions += '<link rel="apple-touch-startup-image" href="' + jQTSettings.startupScreen + '" />'; |
---|
| 99 | } |
---|
| 100 | // Set viewport |
---|
| 101 | if (jQTSettings.fixedViewport) { |
---|
| 102 | hairextensions += '<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"/>'; |
---|
| 103 | } |
---|
| 104 | // Set full-screen |
---|
| 105 | if (jQTSettings.fullScreen) { |
---|
| 106 | hairextensions += '<meta name="apple-mobile-web-app-capable" content="yes" />'; |
---|
| 107 | if (jQTSettings.statusBar) { |
---|
| 108 | hairextensions += '<meta name="apple-mobile-web-app-status-bar-style" content="' + jQTSettings.statusBar + '" />'; |
---|
| 109 | } |
---|
| 110 | } |
---|
| 111 | if (hairextensions) $head.append(hairextensions); |
---|
| 112 | |
---|
| 113 | // Initialize on document load: |
---|
| 114 | $(document).ready(function(){ |
---|
| 115 | |
---|
| 116 | // Add extensions |
---|
| 117 | for (var i in extensions) |
---|
| 118 | { |
---|
| 119 | var fn = extensions[i]; |
---|
| 120 | if ($.isFunction(fn)) |
---|
| 121 | { |
---|
| 122 | $.extend(publicObj, fn(publicObj)); |
---|
| 123 | } |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | // Add animations |
---|
| 127 | for (var i in defaultAnimations) |
---|
| 128 | { |
---|
| 129 | var name = defaultAnimations[i]; |
---|
| 130 | var selector = jQTSettings[name + 'Selector']; |
---|
| 131 | if (typeof(selector) == 'string') { |
---|
| 132 | addAnimation({name:name, selector:selector}); |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | |
---|
| 136 | touchSelectors.push('input'); |
---|
| 137 | touchSelectors.push(jQTSettings.touchSelector); |
---|
| 138 | touchSelectors.push(jQTSettings.backSelector); |
---|
| 139 | touchSelectors.push(jQTSettings.submitSelector); |
---|
| 140 | $(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none'); |
---|
| 141 | $(jQTSettings.backSelector).tap(liveTap); |
---|
| 142 | $(jQTSettings.submitSelector).tap(submitParentForm); |
---|
| 143 | |
---|
| 144 | $body = $('body'); |
---|
| 145 | |
---|
| 146 | if (jQTSettings.fullScreenClass && window.navigator.standalone == true) { |
---|
| 147 | $body.addClass(jQTSettings.fullScreenClass + ' ' + jQTSettings.statusBar); |
---|
| 148 | } |
---|
| 149 | |
---|
| 150 | // Create custom live events |
---|
| 151 | $body |
---|
| 152 | .bind('touchstart', handleTouch) |
---|
| 153 | .bind('orientationchange', updateOrientation) |
---|
| 154 | .trigger('orientationchange') |
---|
| 155 | .submit(submitForm); |
---|
| 156 | |
---|
| 157 | if (jQTSettings.useFastTouch && $.support.touch) |
---|
| 158 | { |
---|
| 159 | $body.click(function(e){ |
---|
| 160 | var $el = $(e.target); |
---|
| 161 | if ($el.attr('target') == '_blank' || $el.attr('rel') == 'external' || $el.is('input[type="checkbox"]')) |
---|
| 162 | { |
---|
| 163 | return true; |
---|
| 164 | } else { |
---|
| 165 | return false; |
---|
| 166 | } |
---|
| 167 | }); |
---|
| 168 | |
---|
| 169 | // This additionally gets rid of form focusses |
---|
| 170 | $body.mousedown(function(e){ |
---|
| 171 | var timeDiff = (new Date()).getTime() - lastAnimationTime; |
---|
| 172 | if (timeDiff < 200) |
---|
| 173 | { |
---|
| 174 | return false; |
---|
| 175 | } |
---|
| 176 | }); |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | // Make sure exactly one child of body has "current" class |
---|
| 180 | if ($('body > .current').length == 0) { |
---|
| 181 | currentPage = $('body > *:first'); |
---|
| 182 | } else { |
---|
| 183 | currentPage = $('body > .current:first'); |
---|
| 184 | $('body > .current').removeClass('current'); |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | // Go to the top of the "current" page |
---|
| 188 | $(currentPage).addClass('current'); |
---|
| 189 | location.hash = $(currentPage).attr('id'); |
---|
| 190 | addPageToHistory(currentPage); |
---|
| 191 | scrollTo(0, 0); |
---|
| 192 | dumbLoopStart(); |
---|
| 193 | }); |
---|
| 194 | } |
---|
| 195 | |
---|
| 196 | // PUBLIC FUNCTIONS |
---|
| 197 | function goBack(to) { |
---|
| 198 | // Init the param |
---|
| 199 | if (hist.length > 1) { |
---|
| 200 | var numberOfPages = Math.min(parseInt(to || 1, 10), hist.length-1); |
---|
| 201 | |
---|
| 202 | // Search through the history for an ID |
---|
| 203 | if( isNaN(numberOfPages) && typeof(to) === "string" && to != '#' ) { |
---|
| 204 | for( var i=1, length=hist.length; i < length; i++ ) { |
---|
| 205 | if( '#' + hist[i].id === to ) { |
---|
| 206 | numberOfPages = i; |
---|
| 207 | break; |
---|
| 208 | } |
---|
| 209 | } |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | // If still nothing, assume one |
---|
| 213 | if( isNaN(numberOfPages) || numberOfPages < 1 ) { |
---|
| 214 | numberOfPages = 1; |
---|
| 215 | }; |
---|
| 216 | |
---|
| 217 | // Grab the current page for the "from" info |
---|
| 218 | var animation = hist[0].animation; |
---|
| 219 | var fromPage = hist[0].page; |
---|
| 220 | |
---|
| 221 | // Remove all pages in front of the target page |
---|
| 222 | hist.splice(0, numberOfPages); |
---|
| 223 | |
---|
| 224 | // Grab the target page |
---|
| 225 | var toPage = hist[0].page; |
---|
| 226 | |
---|
| 227 | // Make the animations |
---|
| 228 | animatePages(fromPage, toPage, animation, true); |
---|
| 229 | |
---|
| 230 | return publicObj; |
---|
| 231 | } else { |
---|
| 232 | console.error('No pages in history.'); |
---|
| 233 | return false; |
---|
| 234 | } |
---|
| 235 | } |
---|
| 236 | function goTo(toPage, animation) { |
---|
| 237 | var fromPage = hist[0].page; |
---|
| 238 | |
---|
| 239 | if (typeof(toPage) === 'string') { |
---|
| 240 | toPage = $(toPage); |
---|
| 241 | } |
---|
| 242 | if (typeof(animation) === 'string') { |
---|
| 243 | for (var i = animations.length - 1; i >= 0; i--){ |
---|
| 244 | if (animations[i].name === animation) |
---|
| 245 | { |
---|
| 246 | animation = animations[i]; |
---|
| 247 | break; |
---|
| 248 | } |
---|
| 249 | } |
---|
| 250 | } |
---|
| 251 | if (animatePages(fromPage, toPage, animation)) { |
---|
| 252 | addPageToHistory(toPage, animation); |
---|
| 253 | return publicObj; |
---|
| 254 | } |
---|
| 255 | else |
---|
| 256 | { |
---|
| 257 | console.error('Could not animate pages.'); |
---|
| 258 | return false; |
---|
| 259 | } |
---|
| 260 | } |
---|
| 261 | function getOrientation() { |
---|
| 262 | return orientation; |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | // PRIVATE FUNCTIONS |
---|
| 266 | function liveTap(e){ |
---|
| 267 | |
---|
| 268 | // Grab the clicked element |
---|
| 269 | var $el = $(e.target); |
---|
| 270 | |
---|
| 271 | if ($el.attr('nodeName')!=='A'){ |
---|
| 272 | $el = $el.parent('a'); |
---|
| 273 | } |
---|
| 274 | |
---|
| 275 | var target = $el.attr('target'), |
---|
| 276 | hash = $el.attr('hash'), |
---|
| 277 | animation=null; |
---|
| 278 | |
---|
| 279 | if (tapReady == false || !$el.length) { |
---|
| 280 | console.warn('Not able to tap element.') |
---|
| 281 | return false; |
---|
| 282 | } |
---|
| 283 | |
---|
| 284 | if ($el.attr('target') == '_blank' || $el.attr('rel') == 'external') |
---|
| 285 | { |
---|
| 286 | return true; |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | // Figure out the animation to use |
---|
| 290 | for (var i = animations.length - 1; i >= 0; i--){ |
---|
| 291 | if ($el.is(animations[i].selector)) { |
---|
| 292 | animation = animations[i]; |
---|
| 293 | break; |
---|
| 294 | } |
---|
| 295 | }; |
---|
| 296 | |
---|
| 297 | // User clicked an internal link, fullscreen mode |
---|
| 298 | if (target == '_webapp') { |
---|
| 299 | window.location = $el.attr('href'); |
---|
| 300 | } |
---|
| 301 | // User clicked a back button |
---|
| 302 | else if ($el.is(jQTSettings.backSelector)) { |
---|
| 303 | goBack(hash); |
---|
| 304 | } |
---|
| 305 | // Branch on internal or external href |
---|
| 306 | else if (hash && hash!='#') { |
---|
| 307 | $el.addClass('active'); |
---|
| 308 | goTo($(hash).data('referrer', $el), animation); |
---|
| 309 | } else { |
---|
| 310 | $el.addClass('loading active'); |
---|
| 311 | showPageByHref($el.attr('href'), { |
---|
| 312 | animation: animation, |
---|
| 313 | callback: function(){ |
---|
| 314 | $el.removeClass('loading'); setTimeout($.fn.unselect, 250, $el); |
---|
| 315 | }, |
---|
| 316 | $referrer: $el |
---|
| 317 | }); |
---|
| 318 | } |
---|
| 319 | return false; |
---|
| 320 | } |
---|
| 321 | function addPageToHistory(page, animation) { |
---|
| 322 | // Grab some info |
---|
| 323 | var pageId = page.attr('id'); |
---|
| 324 | |
---|
| 325 | // Prepend info to page history |
---|
| 326 | hist.unshift({ |
---|
| 327 | page: page, |
---|
| 328 | animation: animation, |
---|
| 329 | id: pageId |
---|
| 330 | }); |
---|
| 331 | } |
---|
| 332 | function animatePages(fromPage, toPage, animation, backwards) { |
---|
| 333 | // Error check for target page |
---|
| 334 | if(toPage.length === 0){ |
---|
| 335 | $.fn.unselect(); |
---|
| 336 | console.error('Target element is missing.'); |
---|
| 337 | return false; |
---|
| 338 | } |
---|
| 339 | |
---|
| 340 | // Collapse the keyboard |
---|
| 341 | $(':focus').blur(); |
---|
| 342 | |
---|
| 343 | // Make sure we are scrolled up to hide location bar |
---|
| 344 | scrollTo(0, 0); |
---|
| 345 | |
---|
| 346 | // Define callback to run after animation completes |
---|
| 347 | var callback = function(event){ |
---|
| 348 | |
---|
| 349 | if (animation) |
---|
| 350 | { |
---|
| 351 | toPage.removeClass('in reverse ' + animation.name); |
---|
| 352 | fromPage.removeClass('current out reverse ' + animation.name); |
---|
| 353 | } |
---|
| 354 | else |
---|
| 355 | { |
---|
| 356 | fromPage.removeClass('current'); |
---|
| 357 | } |
---|
| 358 | |
---|
| 359 | toPage.trigger('pageAnimationEnd', { direction: 'in' }); |
---|
| 360 | fromPage.trigger('pageAnimationEnd', { direction: 'out' }); |
---|
| 361 | |
---|
| 362 | clearInterval(dumbLoop); |
---|
| 363 | currentPage = toPage; |
---|
| 364 | location.hash = currentPage.attr('id'); |
---|
| 365 | dumbLoopStart(); |
---|
| 366 | |
---|
| 367 | var $originallink = toPage.data('referrer'); |
---|
| 368 | if ($originallink) { |
---|
| 369 | $originallink.unselect(); |
---|
| 370 | } |
---|
| 371 | lastAnimationTime = (new Date()).getTime(); |
---|
| 372 | tapReady = true; |
---|
| 373 | } |
---|
| 374 | |
---|
| 375 | fromPage.trigger('pageAnimationStart', { direction: 'out' }); |
---|
| 376 | toPage.trigger('pageAnimationStart', { direction: 'in' }); |
---|
| 377 | |
---|
| 378 | if ($.support.WebKitAnimationEvent && animation && jQTSettings.useAnimations) { |
---|
| 379 | toPage.one('webkitAnimationEnd', callback); |
---|
| 380 | tapReady = false; |
---|
| 381 | toPage.addClass(animation.name + ' in current ' + (backwards ? ' reverse' : '')); |
---|
| 382 | fromPage.addClass(animation.name + ' out' + (backwards ? ' reverse' : '')); |
---|
| 383 | } else { |
---|
| 384 | toPage.addClass('current'); |
---|
| 385 | callback(); |
---|
| 386 | } |
---|
| 387 | |
---|
| 388 | return true; |
---|
| 389 | } |
---|
| 390 | function dumbLoopStart() { |
---|
| 391 | dumbLoop = setInterval(function(){ |
---|
| 392 | var curid = currentPage.attr('id'); |
---|
| 393 | if (location.hash == '') { |
---|
| 394 | location.hash = '#' + curid; |
---|
| 395 | } else if (location.hash != '#' + curid) { |
---|
| 396 | try { |
---|
| 397 | goBack(location.hash) |
---|
| 398 | } catch(e) { |
---|
| 399 | console.error('Unknown hash change.'); |
---|
| 400 | } |
---|
| 401 | } |
---|
| 402 | }, 100); |
---|
| 403 | } |
---|
| 404 | function insertPages(nodes, animation) { |
---|
| 405 | var targetPage = null; |
---|
| 406 | $(nodes).each(function(index, node){ |
---|
| 407 | var $node = $(this); |
---|
| 408 | if (!$node.attr('id')) { |
---|
| 409 | $node.attr('id', 'page-' + (++newPageCount)); |
---|
| 410 | } |
---|
| 411 | $node.appendTo($body); |
---|
| 412 | if ($node.hasClass('current') || !targetPage ) { |
---|
| 413 | targetPage = $node; |
---|
| 414 | } |
---|
| 415 | }); |
---|
| 416 | if (targetPage !== null) { |
---|
| 417 | goTo(targetPage, animation); |
---|
| 418 | return targetPage; |
---|
| 419 | } |
---|
| 420 | else |
---|
| 421 | { |
---|
| 422 | return false; |
---|
| 423 | } |
---|
| 424 | } |
---|
| 425 | function showPageByHref(href, options) { |
---|
| 426 | var defaults = { |
---|
| 427 | data: null, |
---|
| 428 | method: 'GET', |
---|
| 429 | animation: null, |
---|
| 430 | callback: null, |
---|
| 431 | $referrer: null |
---|
| 432 | }; |
---|
| 433 | |
---|
| 434 | var settings = $.extend({}, defaults, options); |
---|
| 435 | |
---|
| 436 | if (href != '#') |
---|
| 437 | { |
---|
| 438 | $.ajax({ |
---|
| 439 | url: href, |
---|
| 440 | data: settings.data, |
---|
| 441 | type: settings.method, |
---|
| 442 | success: function (data, textStatus) { |
---|
| 443 | var firstPage = insertPages(data, settings.animation); |
---|
| 444 | if (firstPage) |
---|
| 445 | { |
---|
| 446 | if (settings.method == 'GET' && jQTSettings.cacheGetRequests && settings.$referrer) |
---|
| 447 | { |
---|
| 448 | settings.$referrer.attr('href', '#' + firstPage.attr('id')); |
---|
| 449 | } |
---|
| 450 | if (settings.callback) { |
---|
| 451 | settings.callback(true); |
---|
| 452 | } |
---|
| 453 | } |
---|
| 454 | }, |
---|
| 455 | error: function (data) { |
---|
| 456 | if (settings.$referrer) settings.$referrer.unselect(); |
---|
| 457 | if (settings.callback) { |
---|
| 458 | settings.callback(false); |
---|
| 459 | } |
---|
| 460 | } |
---|
| 461 | }); |
---|
| 462 | } |
---|
| 463 | else if ($referrer) |
---|
| 464 | { |
---|
| 465 | $referrer.unselect(); |
---|
| 466 | } |
---|
| 467 | } |
---|
| 468 | function submitForm(e, callback){ |
---|
| 469 | var $form = (typeof(e)==='string') ? $(e) : $(e.target); |
---|
| 470 | |
---|
| 471 | if ($form.length && $form.is(jQTSettings.formSelector) && $form.attr('action')) { |
---|
| 472 | showPageByHref($form.attr('action'), { |
---|
| 473 | data: $form.serialize(), |
---|
| 474 | method: $form.attr('method') || "POST", |
---|
| 475 | animation: animations[0] || null, |
---|
| 476 | callback: callback |
---|
| 477 | }); |
---|
| 478 | return false; |
---|
| 479 | } |
---|
| 480 | return true; |
---|
| 481 | } |
---|
| 482 | function submitParentForm(e){ |
---|
| 483 | var $form = $(this).closest('form'); |
---|
| 484 | if ($form.length) |
---|
| 485 | { |
---|
| 486 | evt = jQuery.Event("submit"); |
---|
| 487 | evt.preventDefault(); |
---|
| 488 | $form.trigger(evt); |
---|
| 489 | return false; |
---|
| 490 | } |
---|
| 491 | return true; |
---|
| 492 | } |
---|
| 493 | function addAnimation(animation) { |
---|
| 494 | if (typeof(animation.selector) == 'string' && typeof(animation.name) == 'string') { |
---|
| 495 | animations.push(animation); |
---|
| 496 | $(animation.selector).tap(liveTap); |
---|
| 497 | touchSelectors.push(animation.selector); |
---|
| 498 | } |
---|
| 499 | } |
---|
| 500 | function updateOrientation() { |
---|
| 501 | orientation = window.innerWidth < window.innerHeight ? 'profile' : 'landscape'; |
---|
| 502 | $body.removeClass('profile landscape').addClass(orientation).trigger('turn', {orientation: orientation}); |
---|
| 503 | // scrollTo(0, 0); |
---|
| 504 | } |
---|
| 505 | function handleTouch(e) { |
---|
| 506 | |
---|
| 507 | var $el = $(e.target); |
---|
| 508 | |
---|
| 509 | // Only handle touchSelectors |
---|
| 510 | if (!$(e.target).is(touchSelectors.join(', '))) |
---|
| 511 | { |
---|
| 512 | var $link = $(e.target).closest('a'); |
---|
| 513 | |
---|
| 514 | if ($link.length){ |
---|
| 515 | $el = $link; |
---|
| 516 | } else { |
---|
| 517 | return; |
---|
| 518 | } |
---|
| 519 | } |
---|
| 520 | if (event) |
---|
| 521 | { |
---|
| 522 | var hoverTimeout = null, |
---|
| 523 | startX = event.changedTouches[0].clientX, |
---|
| 524 | startY = event.changedTouches[0].clientY, |
---|
| 525 | startTime = (new Date).getTime(), |
---|
| 526 | deltaX = 0, |
---|
| 527 | deltaY = 0, |
---|
| 528 | deltaT = 0; |
---|
| 529 | |
---|
| 530 | // Let's bind these after the fact, so we can keep some internal values |
---|
| 531 | $el.bind('touchmove', touchmove).bind('touchend', touchend); |
---|
| 532 | |
---|
| 533 | hoverTimeout = setTimeout(function(){ |
---|
| 534 | $el.makeActive(); |
---|
| 535 | }, 100); |
---|
| 536 | |
---|
| 537 | } |
---|
| 538 | |
---|
| 539 | // Private touch functions (TODO: insert dirty joke) |
---|
| 540 | function touchmove(e) { |
---|
| 541 | |
---|
| 542 | updateChanges(); |
---|
| 543 | var absX = Math.abs(deltaX); |
---|
| 544 | var absY = Math.abs(deltaY); |
---|
| 545 | |
---|
| 546 | // Check for swipe |
---|
| 547 | if (absX > absY && (absX > 35) && deltaT < 1000) { |
---|
| 548 | $el.trigger('swipe', {direction: (deltaX < 0) ? 'left' : 'right'}).unbind('touchmove touchend'); |
---|
| 549 | } else if (absY > 1) { |
---|
| 550 | $el.removeClass('active'); |
---|
| 551 | } |
---|
| 552 | |
---|
| 553 | clearTimeout(hoverTimeout); |
---|
| 554 | } |
---|
| 555 | |
---|
| 556 | function touchend(){ |
---|
| 557 | updateChanges(); |
---|
| 558 | |
---|
| 559 | if (deltaY === 0 && deltaX === 0) { |
---|
| 560 | $el.makeActive(); |
---|
| 561 | // New approach: |
---|
| 562 | // Fake the double click? |
---|
| 563 | // TODO: Try with all click events (no tap) |
---|
| 564 | // if (deltaT < 40) |
---|
| 565 | // { |
---|
| 566 | // setTimeout(function(){ |
---|
| 567 | // $el.trigger('touchstart') |
---|
| 568 | // .trigger('touchend'); |
---|
| 569 | // }, 0); |
---|
| 570 | // } |
---|
| 571 | $el.trigger('tap'); |
---|
| 572 | } else { |
---|
| 573 | $el.removeClass('active'); |
---|
| 574 | } |
---|
| 575 | $el.unbind('touchmove touchend'); |
---|
| 576 | clearTimeout(hoverTimeout); |
---|
| 577 | } |
---|
| 578 | |
---|
| 579 | function updateChanges(){ |
---|
| 580 | var first = event.changedTouches[0] || null; |
---|
| 581 | deltaX = first.pageX - startX; |
---|
| 582 | deltaY = first.pageY - startY; |
---|
| 583 | deltaT = (new Date).getTime() - startTime; |
---|
| 584 | } |
---|
| 585 | |
---|
| 586 | } // End touch handler |
---|
| 587 | |
---|
| 588 | // Public jQuery Fns |
---|
| 589 | $.fn.unselect = function(obj) { |
---|
| 590 | if (obj) { |
---|
| 591 | obj.removeClass('active'); |
---|
| 592 | } else { |
---|
| 593 | $('.active').removeClass('active'); |
---|
| 594 | } |
---|
| 595 | } |
---|
| 596 | $.fn.makeActive = function(){ |
---|
| 597 | return $(this).addClass('active'); |
---|
| 598 | } |
---|
| 599 | $.fn.swipe = function(fn) { |
---|
| 600 | if ($.isFunction(fn)) |
---|
| 601 | { |
---|
| 602 | return this.each(function(i, el){ |
---|
| 603 | $(el).bind('swipe', fn); |
---|
| 604 | }); |
---|
| 605 | } |
---|
| 606 | } |
---|
| 607 | $.fn.tap = function(fn){ |
---|
| 608 | if ($.isFunction(fn)) |
---|
| 609 | { |
---|
| 610 | var tapEvent = (jQTSettings.useFastTouch && $.support.touch) ? 'tap' : 'click'; |
---|
| 611 | return $(this).live(tapEvent, fn); |
---|
| 612 | } else { |
---|
| 613 | $(this).trigger('tap'); |
---|
| 614 | } |
---|
| 615 | } |
---|
| 616 | |
---|
| 617 | publicObj = { |
---|
| 618 | getOrientation: getOrientation, |
---|
| 619 | goBack: goBack, |
---|
| 620 | goTo: goTo, |
---|
| 621 | addAnimation: addAnimation, |
---|
| 622 | submitForm: submitForm |
---|
| 623 | } |
---|
| 624 | |
---|
| 625 | return publicObj; |
---|
| 626 | } |
---|
| 627 | |
---|
| 628 | // Extensions directly manipulate the jQTouch object, before it's initialized. |
---|
| 629 | $.jQTouch.prototype.extensions = []; |
---|
| 630 | $.jQTouch.addExtension = function(extension){ |
---|
| 631 | $.jQTouch.prototype.extensions.push(extension); |
---|
| 632 | } |
---|
| 633 | |
---|
| 634 | })(jQuery); |
---|