1/*! 2 * jQuery Mobile Events 3 * by Ben Major (www.ben-major.co.uk) 4 * 5 * Copyright 2011, Ben Major 6 * Licensed under the MIT License: 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 * THE SOFTWARE. 25 * 26 */ 27 28 29(function($) { 30 $.attrFn = $.attrFn || {}; 31 32 // navigator.userAgent.toLowerCase() isn't reliable for Chrome installs 33 // on mobile devices. As such, we will create a boolean isChromeDesktop 34 // The reason that we need to do this is because Chrome annoyingly 35 // purports support for touch events even if the underlying hardware 36 // does not! 37 38 var isChromeDesktop = ((navigator.userAgent.toLowerCase().indexOf('chrome') > -1) && ( 39 (navigator.userAgent.toLowerCase().indexOf('windows') > -1) || 40 (navigator.userAgent.toLowerCase().indexOf('macintosh') > -1) || 41 (navigator.userAgent.toLowerCase().indexOf('linux') > -1) 42 )); 43 44 var settings = { 45 swipe_h_threshold : 50, 46 swipe_v_threshold : 50, 47 taphold_threshold : 750, 48 doubletap_int : 500, 49 50 touch_capable : ('ontouchstart' in document.documentElement && !isChromeDesktop), 51 orientation_support : ('orientation' in window && 'onorientationchange' in window), 52 53 startevent : ('ontouchstart' in document.documentElement && !isChromeDesktop) ? 'touchstart' : 'mousedown', 54 endevent : ('ontouchstart' in document.documentElement && !isChromeDesktop) ? 'touchend' : 'mouseup', 55 moveevent : ('ontouchstart' in document.documentElement && !isChromeDesktop) ? 'touchmove' : 'mousemove', 56 tapevent : ('ontouchstart' in document.documentElement && !isChromeDesktop) ? 'tap' : 'click', 57 scrollevent : ('ontouchstart' in document.documentElement && !isChromeDesktop) ? 'touchmove' : 'scroll', 58 59 hold_timer : null, 60 tap_timer : null 61 }; 62 63 // Add Event shortcuts: 64 $.each(('tapstart tapend tap singletap doubletap taphold swipe swipeup swiperight swipedown swipeleft swipeend scrollstart scrollend orientationchange').split(' '), function(i, name) { 65 $.fn[name] = function(fn) 66 { 67 return fn ? this.bind(name, fn) : this.trigger(name); 68 }; 69 70 $.attrFn[name] = true; 71 }); 72 73 // tapstart Event: 74 $.event.special.tapstart = { 75 setup: function() { 76 var thisObject = this, 77 $this = $(thisObject); 78 79 $this.bind(settings.startevent, function(e) { 80 if(e.which && e.which !== 1) 81 { 82 return false; 83 } 84 else 85 { 86 // Touch event data: 87 var origEvent = e.originalEvent; 88 var touchData = { 89 'position': { 90 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 91 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY, 92 }, 93 'offset': { 94 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 95 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY, 96 }, 97 'time': new Date().getTime(), 98 'target': e.target 99 }; 100 101 triggerCustomEvent(thisObject, 'tapstart', e, touchData); 102 return true; 103 } 104 }); 105 } 106 }; 107 108 // tapend Event: 109 $.event.special.tapend = { 110 setup: function() { 111 var thisObject = this, 112 $this = $(thisObject); 113 114 $this.bind(settings.endevent, function(e) { 115 // Touch event data: 116 var origEvent = e.originalEvent; 117 var touchData = { 118 'position': { 119 'x': (settings.touch_capable) ? origEvent.changedTouches[0].screenX : e.screenX, 120 'y': (settings.touch_capable) ? origEvent.changedTouches[0].screenY : e.screenY 121 }, 122 'offset': { 123 'x': (settings.touch_capable) ? origEvent.changedTouches[0].pageX - origEvent.changedTouches[0].target.offsetLeft : e.offsetX, 124 'y': (settings.touch_capable) ? origEvent.changedTouches[0].pageY - origEvent.changedTouches[0].target.offsetTop : e.offsetY 125 }, 126 'time': new Date().getTime(), 127 'target': e.target 128 }; 129 triggerCustomEvent(thisObject, 'tapend', e, touchData); 130 return true; 131 }); 132 } 133 }; 134 135 // taphold Event: 136 $.event.special.taphold = { 137 setup: function() { 138 var thisObject = this, 139 $this = $(thisObject), 140 origTarget, 141 timer, 142 start_pos = { x : 0, y : 0 }; 143 144 $this.bind(settings.startevent, function(e) { 145 if(e.which && e.which !== 1) 146 { 147 return false;
148 } 149 else 150 { 151 $this.data('tapheld', false); 152 origTarget = e.target; 153 154 var origEvent = e.originalEvent; 155 var start_time = new Date().getTime(), 156 startPosition = { 157 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 158 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY 159 }, 160 startOffset = { 161 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 162 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY 163 }; 164 165 start_pos.x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX; 166 start_pos.y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 167 168 settings.hold_timer = window.setTimeout(function() { 169 170 var end_x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX, 171 end_y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 172 173 if(e.target == origTarget && (start_pos.x == end_x && start_pos.y == end_y)) 174 { 175 $this.data('tapheld', true); 176 177 var end_time = new Date().getTime(), 178 endPosition = { 179 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 180 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY 181 }, 182 endOffset = { 183 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 184 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY 185 }; 186 duration = end_time - start_time; 187 188 // Build the touch data: 189 var touchData = { 190 'startTime': start_time, 191 'endTime': end_time, 192 'startPosition': startPosition, 193 'startOffset': startOffset, 194 'endPosition': endPosition, 195 'endOffset': endOffset, 196 'duration': duration, 197 'target': e.target 198 } 199 200 triggerCustomEvent(thisObject, 'taphold', e, touchData); 201 } 202 }, settings.taphold_threshold); 203 204 return true; 205 } 206 }).bind(settings.endevent, function() { 207 $this.data('tapheld', false); 208 window.clearTimeout(settings.hold_timer); 209 }); 210 } 211 }; 212 213 // doubletap Event: 214 $.event.special.doubletap = { 215 setup: function() { 216 var thisObject = this, 217 $this = $(thisObject), 218 origTarget, 219 action, 220 firstTap; 221 222 $this.bind(settings.startevent, function(e) { 223 if(e.which && e.which !== 1) 224 { 225 return false; 226 } 227 else 228 { 229 $this.data('doubletapped', false); 230 origTarget = e.target; 231 var origEvent = e.originalEvent; 232 firstTap = { 233 'position': { 234 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 235 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY 236 }, 237 'offset': { 238 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 239 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY 240 }, 241 'time': new Date().getTime(), 242 'target': e.target 243 }; 244 245 return true; 246 } 247 }).bind(settings.endevent, function(e) { 248 var now = new Date().getTime(); 249 var lastTouch = $this.data('lastTouch') || now + 1; 250 var delta = now - lastTouch; 251 window.clearTimeout(action); 252 253 if(delta < settings.doubletap_int && delta > 0 && (e.target == origTarget) && delta > 100) 254 { 255 $this.data('doubletapped', true); 256 window.clearTimeout(settings.tap_timer); 257 258 // Now get the current event: 259 var lastTap = { 260 'position': { 261 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 262 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY 263 }, 264 'offset': { 265 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 266 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY 267 }, 268 'time': new Date().getTime(), 269 'target': e.target 270 } 271 272 var touchData = { 273 'firstTap': firstTap, 274 'secondTap': lastTap, 275 'interval': lastTap.time - firstTap.time 276 }; 277 278 triggerCustomEvent(thisObject, 'doubletap', e, touchData); 279 } 280 else 281 { 282 $this.data('lastTouch', now); 283 action = window.setTimeout(function(e){ window.clearTimeout(action); }, settings.doubletap_int, [e]); 284 } 285 $this.data('lastTouch', now); 286 }); 287 } 288 }; 289 290 // singletap Event: 291 // This is used in conjuction with doubletap when both events are needed on the same element 292 $.event.special.singletap = { 293 setup: function() { 294 var thisObject = this, 295 $this = $(thisObject), 296 origTarget = null, 297 startTime = null, 298 start_pos = { x: 0, y: 0 }; 299 300 $this.bind(settings.startevent, function(e) { 301 if(e.which && e.which !== 1) 302 { 303 return false;
304 } 305 else 306 { 307 startTime = new Date().getTime(); 308 origTarget = e.target; 309 310 // Get the start x and y position: 311 start_pos.x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX; 312 start_pos.y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 313 return true; 314 } 315 }).bind(settings.endevent, function(e) { 316 317 if(e.target == origTarget) 318 { 319 // Get the end point: 320 end_pos_x = (e.originalEvent.changedTouches) ? e.originalEvent.changedTouches[0].pageX : e.pageX; 321 end_pos_y = (e.originalEvent.changedTouches) ? e.originalEvent.changedTouches[0].pageY : e.pageY; 322 323 settings.tap_timer = window.setTimeout(function() { 324 if(!$this.data('doubletapped') && !$this.data('tapheld') && (start_pos.x == end_pos_x) && (start_pos.y == end_pos_y)) 325 { 326 var origEvent = e.originalEvent; 327 var touchData = { 328 'position': { 329 'x': (settings.touch_capable) ? origEvent.changedTouches[0].screenX : e.screenX, 330 'y': (settings.touch_capable) ? origEvent.changedTouches[0].screenY : e.screenY, 331 }, 332 'offset': { 333 'x': (settings.touch_capable) ? origEvent.changedTouches[0].pageX - origEvent.changedTouches[0].target.offsetLeft : e.offsetX, 334 'y': (settings.touch_capable) ? origEvent.changedTouches[0].pageY - origEvent.changedTouches[0].target.offsetTop : e.offsetY, 335 }, 336 'time': new Date().getTime(), 337 'target': e.target 338 }; 339 triggerCustomEvent(thisObject, 'singletap', e, touchData); 340 } 341 }, settings.doubletap_int); 342 } 343 }); 344 } 345 }; 346 347 // tap Event: 348 $.event.special.tap = { 349 setup: function() { 350 var thisObject = this, 351 $this = $(thisObject), 352 started = false, 353 origTarget = null, 354 start_time, 355 start_pos = { x : 0, y : 0 }; 356 357 $this.bind(settings.startevent, function(e) { 358 if(e.which && e.which !== 1) 359 { 360 return false; 361 } 362 else 363 { 364 started = true; 365 start_pos.x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX; 366 start_pos.y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 367 start_time = new Date().getTime(); 368 origTarget = e.target; 369 return true; 370 } 371 }).bind(settings.endevent, function(e) { 372 // Only trigger if they've started, and the target matches: 373 var end_x = (e.originalEvent.targetTouches) ? e.originalEvent.changedTouches[0].pageX : e.pageX, 374 end_y = (e.originalEvent.targetTouches) ? e.originalEvent.changedTouches[0].pageY : e.pageY; 375 376 if(origTarget == e.target && started && ((new Date().getTime() - start_time) < settings.taphold_threshold) && (start_pos.x == end_x && start_pos.y == end_y)) 377 { 378 var origEvent = e.originalEvent; 379 var touchData = { 380 'position': { 381 'x': (settings.touch_capable) ? origEvent.changedTouches[0].screenX : e.screenX, 382 'y': (settings.touch_capable) ? origEvent.changedTouches[0].screenY : e.screenY, 383 }, 384 'offset': { 385 'x': (settings.touch_capable) ? origEvent.changedTouches[0].pageX - origEvent.changedTouches[0].target.offsetLeft : e.offsetX, 386 'y': (settings.touch_capable) ? origEvent.changedTouches[0].pageY - origEvent.changedTouches[0].target.offsetTop : e.offsetY, 387 }, 388 'time': new Date().getTime(), 389 'target': e.target 390 }; 391 392 triggerCustomEvent(thisObject, 'tap', e, touchData); 393 } 394 }); 395 } 396 }; 397 398 // swipe Event (also handles swipeup, swiperight, swipedown and swipeleft): 399 $.event.special.swipe = { 400 setup: function() { 401 var thisObject = this, 402 $this = $(thisObject), 403 started = false, 404 hasSwiped = false, 405 originalCoord = { x: 0, y: 0 }, 406 finalCoord = { x: 0, y: 0 }, 407 startEvnt; 408 409 // Screen touched, store the original coordinate 410 function touchStart(e) 411 { 412 originalCoord.x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX; 413 originalCoord.y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 414 finalCoord.x = originalCoord.x; 415 finalCoord.y = originalCoord.y; 416 started = true; 417 var origEvent = e.originalEvent; 418 // Read event data into our startEvt: 419 startEvnt = { 420 'position': { 421 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 422 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY, 423 }, 424 'offset': { 425 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 426 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY, 427 }, 428 'time': new Date().getTime(), 429 'target': e.target 430 }; 431 432 // For some reason, we need to add a 100ms pause in order to trigger swiping 433 // on Playbooks: 434 var dt = new Date(); 435 while ((new Date()) - dt < 100) { } 436 } 437 438 // Store coordinates as finger is swiping 439 function touchMove(e) 440 { 441 finalCoord.x = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageX : e.pageX; 442 finalCoord.y = (e.originalEvent.targetTouches) ? e.originalEvent.targetTouches[0].pageY : e.pageY; 443 window.clearTimeout(settings.hold_timer); 444 445 var swipedir; 446 447 // We need to check if the element to which the event was bound contains a data-xthreshold | data-vthreshold: 448 var ele_x_threshold = $this.attr('data-xthreshold'), 449 ele_y_threshold = $this.attr('data-ythreshold'), 450 h_threshold = (typeof ele_x_threshold !== 'undefined' && ele_x_threshold !== false && parseInt(ele_x_threshold)) ? parseInt(ele_x_threshold) : settings.swipe_h_threshold, 451 v_threshold = (typeof ele_y_threshold !== 'undefined' && ele_y_threshold !== false && parseInt(ele_y_threshold)) ? parseInt(ele_y_threshold) : settings.swipe_v_threshold; 452 453 454 if(originalCoord.y > finalCoord.y && (originalCoord.y - finalCoord.y > v_threshold)) { swipedir = 'swipeup'; } 455 if(originalCoord.x < finalCoord.x && (finalCoord.x - originalCoord.x > h_threshold)) { swipedir = 'swiperight'; }
456 if(originalCoord.y < finalCoord.y && (finalCoord.y - originalCoord.y > v_threshold)) { swipedir = 'swipedown'; } 457 if(originalCoord.x > finalCoord.x && (originalCoord.x - finalCoord.x > h_threshold)) { swipedir = 'swipeleft'; } 458 if(swipedir != undefined && started) 459 { 460 originalCoord.x = 0; 461 originalCoord.y = 0; 462 finalCoord.x = 0; 463 finalCoord.y = 0; 464 started = false; 465 466 // Read event data into our endEvnt: 467 var origEvent = e.originalEvent; 468 endEvnt = { 469 'position': { 470 'x': (settings.touch_capable) ? origEvent.touches[0].screenX : e.screenX, 471 'y': (settings.touch_capable) ? origEvent.touches[0].screenY : e.screenY, 472 }, 473 'offset': { 474 'x': (settings.touch_capable) ? origEvent.touches[0].pageX - origEvent.touches[0].target.offsetLeft : e.offsetX, 475 'y': (settings.touch_capable) ? origEvent.touches[0].pageY - origEvent.touches[0].target.offsetTop : e.offsetY, 476 }, 477 'time': new Date().getTime(), 478 'target': e.target 479 }; 480 481 // Calculate the swipe amount (normalized): 482 var xAmount = Math.abs(startEvnt.position.x - endEvnt.position.x), 483 yAmount = Math.abs(startEvnt.position.y - endEvnt.position.y); 484 485 var touchData = { 486 'startEvnt': startEvnt, 487 'endEvnt': endEvnt, 488 'direction': swipedir.replace('swipe', ''), 489 'xAmount': xAmount, 490 'yAmount': yAmount, 491 'duration': endEvnt.time - startEvnt.time 492 } 493 hasSwiped = true; 494 $this.trigger('swipe', touchData).trigger(swipedir, touchData); 495 } 496 } 497 498 function touchEnd(e) 499 { 500 if(hasSwiped) 501 { 502 // We need to check if the element to which the event was bound contains a data-xthreshold | data-vthreshold: 503 var ele_x_threshold = $this.attr('data-xthreshold'), 504 ele_y_threshold = $this.attr('data-ythreshold'), 505 h_threshold = (typeof ele_x_threshold !== 'undefined' && ele_x_threshold !== false && parseInt(ele_x_threshold)) ? parseInt(ele_x_threshold) : settings.swipe_h_threshold, 506 v_threshold = (typeof ele_y_threshold !== 'undefined' && ele_y_threshold !== false && parseInt(ele_y_threshold)) ? parseInt(ele_y_threshold) : settings.swipe_v_threshold; 507 508 var origEvent = e.originalEvent; 509 endEvnt = { 510 'position': { 511 'x': (settings.touch_capable) ? origEvent.changedTouches[0].screenX : e.screenX, 512 'y': (settings.touch_capable) ? origEvent.changedTouches[0].screenY : e.screenY, 513 }, 514 'offset': { 515 'x': (settings.touch_capable) ? origEvent.changedTouches[0].pageX - origEvent.changedTouches[0].target.offsetLeft : e.offsetX, 516 'y': (settings.touch_capable) ? origEvent.changedTouches[0].pageY - origEvent.changedTouches[0].target.offsetTop : e.offsetY, 517 }, 518 'time': new Date().getTime(), 519 'target': e.target 520 }; 521 522 // Read event data into our endEvnt: 523 if(startEvnt.position.y > endEvnt.position.y && (startEvnt.position.y - endEvnt.position.y > v_threshold)) { swipedir = 'swipeup'; } 524 if(startEvnt.position.x < endEvnt.position.x && (endEvnt.position.x - startEvnt.position.x > h_threshold)) { swipedir = 'swiperight'; } 525 if(startEvnt.position.y < endEvnt.position.y && (endEvnt.position.y - startEvnt.position.y > v_threshold)) { swipedir = 'swipedown'; } 526 if(startEvnt.position.x > endEvnt.position.x && (startEvnt.position.x - endEvnt.position.x > h_threshold)) { swipedir = 'swipeleft'; } 527 528 // Calculate the swipe amount (normalized): 529 var xAmount = Math.abs(startEvnt.position.x - endEvnt.position.x), 530 yAmount = Math.abs(startEvnt.position.y - endEvnt.position.y); 531 532 var touchData = { 533 'startEvnt': startEvnt, 534 'endEvnt': endEvnt, 535 'direction': swipedir.replace('swipe', ''), 536 'xAmount': xAmount, 537 'yAmount': yAmount, 538 'duration': endEvnt.time - startEvnt.time 539 } 540 $this.trigger('swipeend', touchData); 541 } 542 543 started = false; 544 hasSwiped = false; 545 } 546 547 $this.bind(settings.startevent, touchStart); 548 $this.bind(settings.moveevent, touchMove); 549 $this.bind(settings.endevent, touchEnd); 550 } 551 }; 552 553 // scrollstart Event (also handles scrollend): 554 $.event.special.scrollstart = { 555 setup: function() { 556 var thisObject = this, 557 $this = $(thisObject), 558 scrolling, 559 timer; 560 561 function trigger(event, state) 562 { 563 scrolling = state; 564 triggerCustomEvent(thisObject, scrolling ? 'scrollstart' : 'scrollend', event); 565 } 566 567 // iPhone triggers scroll after a small delay; use touchmove instead 568 $this.bind(settings.scrollevent, function(event) { 569 if(!scrolling) 570 { 571 trigger(event, true); 572 } 573 574 clearTimeout(timer); 575 timer = setTimeout(function() { trigger(event, false); }, 50); 576 }); 577 } 578 }; 579 580 // This is the orientation change (largely borrowed from jQuery Mobile): 581 var win = $(window), 582 special_event, 583 get_orientation, 584 last_orientation, 585 initial_orientation_is_landscape, 586 initial_orientation_is_default, 587 portrait_map = { '0': true, '180': true }; 588 589 if(settings.orientation_support) 590 { 591 var ww = window.innerWidth || $(window).width(), 592 wh = window.innerHeight || $(window).height(), 593 landscape_threshold = 50; 594 595 initial_orientation_is_landscape = ww > wh && (ww - wh) > landscape_threshold; 596 initial_orientation_is_default = portrait_map[window.orientation]; 597 598 if((initial_orientation_is_landscape && initial_orientation_is_default) || (!initial_orientation_is_landscape && !initial_orientation_is_default)) 599 { 600 portrait_map = { '-90': true, '90': true }; 601 } 602 } 603 604 $.event.special.orientationchange = special_event = { 605 setup: function() { 606 // If the event is supported natively, return false so that jQuery 607 // will bind to the event using DOM methods. 608 if(settings.orientation_support) 609 { 610 return false;
611 } 612 613 // Get the current orientation to avoid initial double-triggering. 614 last_orientation = get_orientation(); 615 616 win.bind('throttledresize', handler); 617 return true; 618 }, 619 teardown: function() 620 { 621 if (settings.orientation_support) 622 { 623 return false; 624 } 625 626 win.unbind('throttledresize', handler); 627 return true; 628 }, 629 add: function(handleObj) 630 { 631 // Save a reference to the bound event handler. 632 var old_handler = handleObj.handler; 633 634 handleObj.handler = function(event) 635 { 636 event.orientation = get_orientation(); 637 return old_handler.apply(this, arguments); 638 }; 639 } 640 }; 641 642 // If the event is not supported natively, this handler will be bound to 643 // the window resize event to simulate the orientationchange event. 644 function handler() 645 { 646 // Get the current orientation. 647 var orientation = get_orientation(); 648 649 if(orientation !== last_orientation) 650 { 651 // The orientation has changed, so trigger the orientationchange event. 652 last_orientation = orientation; 653 win.trigger( "orientationchange" ); 654 } 655 } 656 657 $.event.special.orientationchange.orientation = get_orientation = function() { 658 var isPortrait = true, 659 elem = document.documentElement; 660 661 if(settings.orientation_support) 662 { 663 isPortrait = portrait_map[window.orientation]; 664 } 665 else 666 { 667 isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1; 668 } 669 670 return isPortrait ? 'portrait' : 'landscape'; 671 }; 672 673 // throttle Handler: 674 $.event.special.throttledresize = { 675 setup: function() 676 { 677 $(this).bind('resize', throttle_handler); 678 }, 679 teardown: function() 680 { 681 $(this).unbind('resize', throttle_handler); 682 } 683 }; 684 685 var throttle = 250, 686 throttle_handler = function() 687 { 688 curr = (new Date()).getTime(); 689 diff = curr - lastCall; 690 691 if(diff >= throttle) 692 { 693 lastCall = curr; 694 $(this).trigger('throttledresize'); 695 696 } 697 else 698 { 699 if(heldCall) 700 { 701 window.clearTimeout(heldCall); 702 } 703 704 // Promise a held call will still execute 705 heldCall = window.setTimeout(handler, throttle - diff); 706 } 707 }, 708 lastCall = 0, 709 heldCall, 710 curr, 711 diff; 712 713 // Trigger a custom event: 714 function triggerCustomEvent( obj, eventType, event, touchData ) { 715 var originalType = event.type; 716 event.type = eventType; 717 718 $.event.dispatch.call( obj, event, touchData ); 719 event.type = originalType; 720 } 721 722 // Correctly bind anything we've overloaded: 723 $.each({ 724 scrollend: 'scrollstart', 725 swipeup: 'swipe', 726 swiperight: 'swipe', 727 swipedown: 'swipe', 728 swipeleft: 'swipe', 729 swipeend: 'swipe', 730 }, function(e, srcE, touchData) { 731 $.event.special[e] = 732 { 733 setup: function() { 734 $(this).bind(srcE, $.noop); 735 } 736 }; 737 }); 738 739}) (jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.