1/* 2* creativeJ Mobile v1.4.5 3* http://creativeJmobile.com 4* 5* Copyright 2010, 2014 creativeJ Foundation, Inc. and other contributors 6* Released under the MIT license. 7* http://creativeJ.org/license 8* 9*/ 10 11(function ( root, doc, factory ) { 12 if ( typeof define === "function" && define.amd ) { 13 // AMD. Register as an anonymous module. 14 define( [ "creativeJ" ], function ( $ ) { 15 factory( $, root, doc ); 16 return $.mobile; 17 }); 18 } else { 19 // Browser globals 20 factory( root.creativeJ, root, doc ); 21 } 22}( this, document, function ( creativeJ, window, document, undefined ) {// This plugin is an experiment for abstracting away the touch and mouse 23// events so that developers don't have to worry about which method of input 24// the device their document is loaded on supports. 25// 26// The idea here is to allow the developer to register listeners for the 27// basic mouse events, such as mousedown, mousemove, mouseup, and click, 28// and the plugin will take care of registering the correct listeners 29// behind the scenes to invoke the listener at the fastest possible time 30// for that device, while still retaining the order of event firing in 31// the traditional mouse environment, should multiple handlers be registered 32// on the same element for different events. 33// 34// The current version exposes the following virtual events to creativeJ bind methods: 35// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel" 36 37(function( $, window, document, undefined ) { 38 39var dataPropertyName = "virtualMouseBindings", 40 touchTargetPropertyName = "virtualTouchID", 41 virtualEventNames = "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split( " " ), 42 touchEventProps = "clientX clientY pageX pageY screenX screenY".split( " " ), 43 mouseHookProps = $.event.mouseHooks ? $.event.mouseHooks.props : [], 44 mouseEventProps = $.event.props.concat( mouseHookProps ), 45 activeDocHandlers = {}, 46 resetTimerID = 0, 47 startX = 0, 48 startY = 0, 49 didScroll = false, 50 clickBlockList = [], 51 blockMouseTriggers = false, 52 blockTouchTriggers = false, 53 eventCaptureSupported = "addEventListener" in document, 54 $document = $( document ), 55 nextTouchID = 1, 56 lastTouchID = 0, threshold, 57 i; 58 59$.vmouse = { 60 moveDistanceThreshold: 10, 61 clickDistanceThreshold: 10, 62 resetTimerDuration: 1500 63}; 64 65function getNativeEvent( event ) { 66 67 while ( event && typeof event.originalEvent !== "undefined" ) { 68 event = event.originalEvent; 69 } 70 return event; 71} 72 73function createVirtualEvent( event, eventType ) { 74 75 var t = event.type, 76 oe, props, ne, prop, ct, touch, i, j, len; 77 78 event = $.Event( event ); 79 event.type = eventType; 80 81 oe = event.originalEvent; 82 props = $.event.props; 83 84 // addresses separation of $.event.props in to $.event.mouseHook.props and Issue 3280 85 // https://github.com/creativeJ/creativeJ-mobile/issues/3280 86 if ( t.search( /^(mouse|click)/ ) > -1 ) { 87 props = mouseEventProps; 88 } 89 90 // copy original event properties over to the new event 91 // this would happen if we could call $.event.fix instead of $.Event 92 // but we don't have a way to force an event to be fixed multiple times 93 if ( oe ) { 94 for ( i = props.length, prop; i; ) { 95 prop = props[ --i ]; 96 event[ prop ] = oe[ prop ]; 97 } 98 } 99 100 // make sure that if the mouse and click virtual events are generated 101 // without a .which one is defined 102 if ( t.search(/mouse(down|up)|click/) > -1 && !event.which ) { 103 event.which = 1; 104 } 105 106 if ( t.search(/^touch/) !== -1 ) { 107 ne = getNativeEvent( oe ); 108 t = ne.touches; 109 ct = ne.changedTouches; 110 touch = ( t && t.length ) ? t[0] : ( ( ct && ct.length ) ? ct[ 0 ] : undefined ); 111 112 if ( touch ) { 113 for ( j = 0, len = touchEventProps.length; j < len; j++) { 114 prop = touchEventProps[ j ]; 115 event[ prop ] = touch[ prop ]; 116 } 117 } 118 } 119 120 return event; 121} 122 123function getVirtualBindingFlags( element ) { 124 125 var flags = {}, 126 b, k; 127 128 while ( element ) { 129 130 b = $.data( element, dataPropertyName ); 131 132 for ( k in b ) { 133 if ( b[ k ] ) { 134 flags[ k ] = flags.hasVirtualBinding = true; 135 } 136 } 137 element = element.parentNode; 138 } 139 return flags; 140} 141 142function getClosestElementWithVirtualBinding( element, eventType ) { 143 var b; 144 while ( element ) { 145 146 b = $.data( element, dataPropertyName ); 147 148 if ( b && ( !eventType || b[ eventType ] ) ) { 149 return element; 150 } 151 element = element.parentNode; 152 } 153 return null; 154} 155 156function enableTouchBindings() { 157 blockTouchTriggers = false;
158} 159 160function disableTouchBindings() { 161 blockTouchTriggers = true; 162} 163 164function enableMouseBindings() { 165 lastTouchID = 0; 166 clickBlockList.length = 0; 167 blockMouseTriggers = false; 168 169 // When mouse bindings are enabled, our 170 // touch bindings are disabled. 171 disableTouchBindings(); 172} 173 174function disableMouseBindings() { 175 // When mouse bindings are disabled, our 176 // touch bindings are enabled. 177 enableTouchBindings(); 178} 179 180function startResetTimer() { 181 clearResetTimer(); 182 resetTimerID = setTimeout( function() { 183 resetTimerID = 0; 184 enableMouseBindings(); 185 }, $.vmouse.resetTimerDuration ); 186} 187 188function clearResetTimer() { 189 if ( resetTimerID ) { 190 clearTimeout( resetTimerID ); 191 resetTimerID = 0; 192 } 193} 194 195function triggerVirtualEvent( eventType, event, flags ) { 196 var ve; 197 198 if ( ( flags && flags[ eventType ] ) || 199 ( !flags && getClosestElementWithVirtualBinding( event.target, eventType ) ) ) { 200 201 ve = createVirtualEvent( event, eventType ); 202 203 $( event.target).trigger( ve ); 204 } 205 206 return ve; 207} 208 209function mouseEventCallback( event ) { 210 var touchID = $.data( event.target, touchTargetPropertyName ), 211 ve; 212 213 if ( !blockMouseTriggers && ( !lastTouchID || lastTouchID !== touchID ) ) { 214 ve = triggerVirtualEvent( "v" + event.type, event ); 215 if ( ve ) { 216 if ( ve.isDefaultPrevented() ) { 217 event.preventDefault(); 218 } 219 if ( ve.isPropagationStopped() ) { 220 event.stopPropagation(); 221 } 222 if ( ve.isImmediatePropagationStopped() ) { 223 event.stopImmediatePropagation(); 224 } 225 } 226 } 227} 228 229function handleTouchStart( event ) { 230 231 var touches = getNativeEvent( event ).touches, 232 target, flags, t; 233 234 if ( touches && touches.length === 1 ) { 235 236 target = event.target; 237 flags = getVirtualBindingFlags( target ); 238 239 if ( flags.hasVirtualBinding ) { 240 241 lastTouchID = nextTouchID++; 242 $.data( target, touchTargetPropertyName, lastTouchID ); 243 244 clearResetTimer(); 245 246 disableMouseBindings(); 247 didScroll = false; 248 249 t = getNativeEvent( event ).touches[ 0 ]; 250 startX = t.pageX; 251 startY = t.pageY; 252 253 triggerVirtualEvent( "vmouseover", event, flags ); 254 triggerVirtualEvent( "vmousedown", event, flags ); 255 } 256 } 257} 258 259function handleScroll( event ) { 260 if ( blockTouchTriggers ) { 261 return; 262 } 263 264 if ( !didScroll ) { 265 triggerVirtualEvent( "vmousecancel", event, getVirtualBindingFlags( event.target ) ); 266 } 267 268 didScroll = true; 269 startResetTimer(); 270} 271 272function handleTouchMove( event ) { 273 if ( blockTouchTriggers ) { 274 return; 275 } 276 277 var t = getNativeEvent( event ).touches[ 0 ], 278 didCancel = didScroll, 279 moveThreshold = $.vmouse.moveDistanceThreshold, 280 flags = getVirtualBindingFlags( event.target ); 281 282 didScroll = didScroll || 283 ( Math.abs( t.pageX - startX ) > moveThreshold || 284 Math.abs( t.pageY - startY ) > moveThreshold ); 285 286 if ( didScroll && !didCancel ) { 287 triggerVirtualEvent( "vmousecancel", event, flags ); 288 } 289 290 triggerVirtualEvent( "vmousemove", event, flags ); 291 startResetTimer(); 292} 293 294function handleTouchEnd( event ) { 295 if ( blockTouchTriggers ) { 296 return; 297 } 298 299 disableTouchBindings(); 300 301 var flags = getVirtualBindingFlags( event.target ), 302 ve, t; 303 triggerVirtualEvent( "vmouseup", event, flags ); 304 305 if ( !didScroll ) { 306 ve = triggerVirtualEvent( "vclick", event, flags ); 307 if ( ve && ve.isDefaultPrevented() ) { 308 // The target of the mouse events that follow the touchend 309 // event don't necessarily match the target used during the 310 // touch. This means we need to rely on coordinates for blocking 311 // any click that is generated. 312 t = getNativeEvent( event ).changedTouches[ 0 ]; 313 clickBlockList.push({ 314 touchID: lastTouchID, 315 x: t.clientX, 316 y: t.clientY 317 }); 318 319 // Prevent any mouse events that follow from triggering 320 // virtual event notifications. 321 blockMouseTriggers = true; 322 } 323 } 324 triggerVirtualEvent( "vmouseout", event, flags); 325 didScroll = false; 326 327 startResetTimer(); 328} 329 330function hasVirtualBindings( ele ) { 331 var bindings = $.data( ele, dataPropertyName ), 332 k; 333 334 if ( bindings ) { 335 for ( k in bindings ) { 336 if ( bindings[ k ] ) { 337 return true; 338 } 339 } 340 } 341 return false; 342} 343 344function dummyMouseHandler() {} 345 346function getSpecialEventObject( eventType ) { 347 var realType = eventType.substr( 1 ); 348 349 return { 350 setup: function(/* data, namespace */) { 351 // If this is the first virtual mouse binding for this element, 352 // add a bindings object to its data. 353 354 if ( !hasVirtualBindings( this ) ) { 355 $.data( this, dataPropertyName, {} ); 356 } 357 358 // If setup is called, we know it is the first binding for this 359 // eventType, so initialize the count for the eventType to zero. 360 var bindings = $.data( this, dataPropertyName ); 361 bindings[ eventType ] = true; 362 363 // If this is the first virtual mouse event for this type, 364 // register a global handler on the document. 365 366 activeDocHandlers[ eventType ] = ( activeDocHandlers[ eventType ] || 0 ) + 1; 367 368 if ( activeDocHandlers[ eventType ] === 1 ) { 369 $document.bind( realType, mouseEventCallback ); 370 } 371 372 // Some browsers, like Opera Mini, won't dispatch mouse/click events 373 // for elements unless they actually have handlers registered on them. 374 // To get around this, we register dummy handlers on the elements. 375 376 $( this ).bind( realType, dummyMouseHandler ); 377 378 // For now, if event capture is not supported, we rely on mouse handlers. 379 if ( eventCaptureSupported ) { 380 // If this is the first virtual mouse binding for the document, 381 // register our touchstart handler on the document. 382 383 activeDocHandlers[ "touchstart" ] = ( activeDocHandlers[ "touchstart" ] || 0) + 1; 384 385 if ( activeDocHandlers[ "touchstart" ] === 1 ) { 386 $document.bind( "touchstart", handleTouchStart ) 387 .bind( "touchend", handleTouchEnd ) 388 389 // On touch platforms, touching the screen and then dragging your finger 390 // causes the window content to scroll after some distance threshold is 391 // exceeded. On these platforms, a scroll prevents a click event from being 392 // dispatched, and on some platforms, even the touchend is suppressed. To 393 // mimic the suppression of the click event, we need to watch for a scroll 394 // event. Unfortunately, some platforms like iOS don't dispatch scroll
395 // events until *AFTER* the user lifts their finger (touchend). This means 396 // we need to watch both scroll and touchmove events to figure out whether 397 // or not a scroll happenens before the touchend event is fired. 398 399 .bind( "touchmove", handleTouchMove ) 400 .bind( "scroll", handleScroll ); 401 } 402 } 403 }, 404 405 teardown: function(/* data, namespace */) { 406 // If this is the last virtual binding for this eventType, 407 // remove its global handler from the document. 408 409 --activeDocHandlers[ eventType ]; 410 411 if ( !activeDocHandlers[ eventType ] ) { 412 $document.unbind( realType, mouseEventCallback ); 413 } 414 415 if ( eventCaptureSupported ) { 416 // If this is the last virtual mouse binding in existence, 417 // remove our document touchstart listener. 418 419 --activeDocHandlers[ "touchstart" ]; 420 421 if ( !activeDocHandlers[ "touchstart" ] ) { 422 $document.unbind( "touchstart", handleTouchStart ) 423 .unbind( "touchmove", handleTouchMove ) 424 .unbind( "touchend", handleTouchEnd ) 425 .unbind( "scroll", handleScroll ); 426 } 427 } 428 429 var $this = $( this ), 430 bindings = $.data( this, dataPropertyName ); 431 432 // teardown may be called when an element was 433 // removed from the DOM. If this is the case, 434 // creativeJ core may have already stripped the element 435 // of any data bindings so we need to check it before 436 // using it. 437 if ( bindings ) { 438 bindings[ eventType ] = false; 439 } 440 441 // Unregister the dummy event handler. 442 443 $this.unbind( realType, dummyMouseHandler ); 444 445 // If this is the last virtual mouse binding on the 446 // element, remove the binding data from the element. 447 448 if ( !hasVirtualBindings( this ) ) { 449 $this.removeData( dataPropertyName ); 450 } 451 } 452 }; 453} 454 455// Expose our custom events to the creativeJ bind/unbind mechanism. 456 457for ( i = 0; i < virtualEventNames.length; i++ ) { 458 $.event.special[ virtualEventNames[ i ] ] = getSpecialEventObject( virtualEventNames[ i ] ); 459} 460 461// Add a capture click handler to block clicks. 462// Note that we require event capture support for this so if the device 463// doesn't support it, we punt for now and rely solely on mouse events. 464if ( eventCaptureSupported ) { 465 document.addEventListener( "click", function( e ) { 466 var cnt = clickBlockList.length, 467 target = e.target, 468 x, y, ele, i, o, touchID; 469 470 if ( cnt ) { 471 x = e.clientX; 472 y = e.clientY; 473 threshold = $.vmouse.clickDistanceThreshold; 474 475 // The idea here is to run through the clickBlockList to see if 476 // the current click event is in the proximity of one of our 477 // vclick events that had preventDefault() called on it. If we find 478 // one, then we block the click. 479 // 480 // Why do we have to rely on proximity? 481 // 482 // Because the target of the touch event that triggered the vclick 483 // can be different from the target of the click event synthesized 484 // by the browser. The target of a mouse/click event that is synthesized 485 // from a touch event seems to be implementation specific. For example, 486 // some browsers will fire mouse/click events for a link that is near 487 // a touch event, even though the target of the touchstart/touchend event 488 // says the user touched outside the link. Also, it seems that with most 489 // browsers, the target of the mouse/click event is not calculated until the 490 // time it is dispatched, so if you replace an element that you touched 491 // with another element, the target of the mouse/click will be the new 492 // element underneath that point. 493 // 494 // Aside from proximity, we also check to see if the target and any 495 // of its ancestors were the ones that blocked a click. This is necessary 496 // because of the strange mouse/click target calculation done in the 497 // Android 2.1 browser, where if you click on an element, and there is a 498 // mouse/click handler on one of its ancestors, the target will be the 499 // innermost child of the touched element, even if that child is no where 500 // near the point of touch. 501 502 ele = target; 503 504 while ( ele ) { 505 for ( i = 0; i < cnt; i++ ) { 506 o = clickBlockList[ i ]; 507 touchID = 0; 508 509 if ( ( ele === target && Math.abs( o.x - x ) < threshold && Math.abs( o.y - y ) < threshold ) || 510 $.data( ele, touchTargetPropertyName ) === o.touchID ) { 511 // XXX: We may want to consider removing matches from the block list 512 // instead of waiting for the reset timer to fire. 513 e.preventDefault(); 514 e.stopPropagation(); 515 return; 516 } 517 } 518 ele = ele.parentNode; 519 } 520 } 521 }, true); 522} 523}
523)( creativeJ, window, document ); 524 525 526}));
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.