1 /** 2 * 5.[Browser part]: event 扩展包 3 */ 4 Jx().$package(function(J){ 5 var $E, 6 addEventListener, 7 addOriginalEventListener, 8 removeEventListener, 9 removeOriginalEventListener, 10 customEvent, 11 customEventHandlers=[], 12 onDomReady, 13 isDomReady, 14 Publish, 15 addObserver, 16 addObservers, 17 notifyObservers, 18 removeObserver, 19 standardizeEvent, 20 packageContext=this; 21 /** 22 * event 名字空间 23 * 24 * @namespace 25 * @name event 26 */ 27 J.event = J.event || {}; 28 29 $E = J.event; 30 /* 31 经典的彩蛋必备代码:老外称之为 Tweetable Konami code 32 [上上下下左右左右BA] 33 var k=[]; 34 addEventListener("keyup",function(e){ 35 k.push(e.keyCode); 36 if(k.toString().indexOf("38,38,40,40,37,39,37,39,66,65")>=0){ 37 cheat(); 38 } 39 },true); 40 41 什么不知道 Konami Code? 只能说明你没童年了 - -! 42 http://en.wikipedia.org/wiki/Konami_Code 43 */ 44 //az 45 /** 46 * standardize the ie event 47 * @ignore 48 */ 49 standardizeEvent = function(e, element){ 50 if(!e){ 51 e = window.event; 52 } 53 var element = element || e.srcElement; 54 var eventDocument = document, 55 doc = eventDocument.documentElement, 56 body = eventDocument.body; 57 /** 58 * @ignore 59 */ 60 var event = 61 /** 62 * @ignore 63 */ 64 { 65 _event: e,// In case we really want the IE event object 66 67 type: e.type, // Event type 68 target: e.srcElement, // Where the event happened 69 currentTarget: element, // Where we're handling it 70 relatedTarget: e.fromElement ? e.fromElement : e.toElement, 71 eventPhase: (e.srcElement == element) ? 2 : 3, 72 73 // Mouse coordinates 74 clientX: e.clientX, 75 clientY: e.clientY, 76 screenX: e.screenX, 77 screenY: e.screenY, 78 layerX: e.offsetX, 79 layerY: e.offsetY, 80 pageX: e.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0), 81 pageY: e.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0), 82 wheelDelta: e.wheelDelta, 83 84 // Key state 85 altKey: e.altKey, 86 ctrlKey: e.ctrlKey, 87 shiftKey: e.shiftKey, 88 //原有的charCode 89 charCode: e.keyCode, 90 91 //keyCode 92 keyCode: e.keyCode, 93 /* 94 * keyCode 值附表: 95 * =============================== 96 * 97 * 1.主键盘区字母和数字键的键码值 98 * 按键 键码 99 * 0 48 100 * 1 49 101 * 2 50 102 * 3 51 103 * 4 52 104 * 5 53 105 * 6 54 106 * 7 55 107 * 8 56 108 * 9 57 109 * 110 * A 65 111 * B 66 112 * C 67 113 * D 68 114 * E 69 115 * F 70 116 * G 71 117 * H 72 118 * I 73 119 * J 74 120 * K 75 121 * L 76 122 * M 77 123 * N 78 124 * O 79 125 * P 80 126 * Q 81 127 * R 82 128 * S 83 129 * T 84 130 * U 85 131 * V 86 132 * W 87 133 * X 88 134 * Y 89 135 * Z 90 136 * 137 * 138 * 3.控制键键码值 139 * 按键 键码 140 * BackSpace 8 141 * Tab 9 142 * Clear 12 143 * Enter 13 144 * Shift 16 145 * Control 17 146 * Alt 18 147 * Cape Lock 20 148 * Esc 27 149 * Spacebar 32 150 * Page Up 33 151 * Page Down 34 152 * End 35 153 * Home 36 154 * Left Arrow 37 155 * Up Arrow 38 156 * Right Arrow 39 157 * Down Arrow 40 158 * Insert 45 159 * Delete 46 160 * 161 * Num Lock 144 162 * 163 * ;: 186 164 * =+ 187 165 * ,< 188 166 * -_ 189 167 * .> 190 168 * /? 191 169 * `~ 192 170 * 171 * [{ 219 172 * \| 220 173 * }] 221 174 * ’" 222 175 * 176 * 2.功能键键码值 177 * F1 112 178 * F2 113 179 * F3 114 180 * F4 115 181 * F5 116 182 * F6 117 183 * F7 118 184 * F8 119 185 * F9 120 186 * F10 121 187 * F11 122 188 * F12 123 189 * 190 * 2.数字键盘上的键的键码值 191 * 按键 键码 192 * 0 96 193 * 1 97 194 * 2 98 195 * 3 99 196 * 4 100 197 * 5 101 198 * 6 102 199 * 7 103 200 * 8 104 201 * 9 105 202 * 203 * * 106 204 * + 107 205 * Enter108 206 * - 109 207 * . 110 208 * / 111 209 * 210 */ 211 /** 212 * @ignore 213 */ 214 stopPropagation: function(){ 215 this._event.cancelBubble = true; 216 }, 217 /** 218 * @ignore 219 */ 220 preventDefault: function(){ 221 this._event.returnValue = false; 222 } 223 } 224 /* 225 * relatedTarget 事件属性返回与事件的目标节点相关的节点。 226 * 对于 mouseover 事件来说,该属性是鼠标指针移到目标节点上时所离开的那个节点。 227 * 对于 mouseout 事件来说,该属性是离开目标时,鼠标指针进入的节点。 228 * 对于其他类型的事件来说,这个属性没有用。 229 * az 2011/3/11 230 */ 231 var eventType = e.type.toLowerCase(); 232 if(eventType == 'mouseover'){ 233 event.relatedTarget = e.fromElement; 234 }else if(eventType == 'mouseout'){ 235 event.relatedTarget = e.toElement; 236 } 237 238 if(!J.isUndefined(e.button)){ 239 var v = e.button; 240 var btnCodeMap = { 241 0: -1,//取消原来的值 242 1: 0,//左键 243 2: 2,//右键 244 3: -1,//取消原来的值 245 4: 1//中键 246 }; 247 /* 248 * ie 的鼠标按键值 249 * 0 没按键 250 * 1 按左键 251 * 2 按右键 252 * 3 按左右键 253 * 4 按中间键 254 * 5 按左键和中间键 255 * 6 按右键和中间键 256 * 7 按所有的键 257 */ 258 if(!J.isUndefined(btnCodeMap[v])){ 259 event.button = btnCodeMap[v]; 260 }else{ 261 event.button = v; 262 } 263 } 264 return event; 265 }; 266 267 // From: David Flanagan. 268 269 // In DOM-compliant browsers, our functions are trivial wrappers around 270 // addEventListener( ) and removeEventListener( ). 271 if (document.addEventListener) { 272 /** 273 * 274 * 添加事件监听器 275 * 276 * @method addEventListener 277 * @memberOf event 278 * 279 * @param element 元素 280 * @param eventType 事件类型,不含on 281 * @param handler 事件处理器 282 * @return {Element} 返回元素 283 */ 284 addEventListener = function(element, eventType, handler, options) { 285 //var id = $E._uid( ); // Generate a unique property name 286 if(customEvent["on"+eventType]){ 287 customEvent["on"+eventType](element, eventType, handler, options); 288 return; 289 } 290 addOriginalEventListener(element, eventType, handler); 291 }; 292 /** 293 * @ignore 294 */ 295 addOriginalEventListener = function(element, eventType, handler) { 296 var isExist = false; 297 if(!element){ 298 J.out('targetModel undefined:'+eventType+handler); 299 } 300 if(!element._eventTypes){ 301 element._eventTypes = {}; 302 } 303 if (!element._eventTypes[eventType]){ 304 element._eventTypes[eventType] = []; 305 } 306 element.addEventListener(eventType, handler, false); 307 308 var handlers= element._eventTypes[eventType]; 309 for(var i=0; i<handlers.length; i++){ 310 if(handlers[i] == handler){ 311 isExist = true; 312 break; 313 } 314 } 315 if(!isExist){ 316 handlers.push(handler); 317 } 318 }; 319 320 /** 321 * 322 * 移除事件监听器 323 * 324 * @memberOf event 325 * @method removeEventListener 326 * 327 * @param element 元素 328 * @param eventType 事件类型,不含on 329 * @param handler 事件处理器 330 * @return {Element} 返回元素 331 */ 332 removeEventListener = function(element, eventType, handler) { 333 if(customEvent["off"+eventType]){ 334 customEvent["off"+eventType](element, eventType,handler); 335 return; 336 } 337 if(arguments.length == 3){ 338 removeOriginalEventListener(element, eventType, handler); 339 }else{ 340 removeOriginalEventListener(element, eventType); 341 } 342 }; 343 /** 344 * @ignore 345 */ 346 removeOriginalEventListener = function(element, eventType, handler) { 347 if(eventType){ 348 if(arguments.length == 3){//修复传入了第三个参数,但是第三个参数为 undefined 的问题 349 if(handler){ 350 element.removeEventListener(eventType, handler, false); 351 if(element._eventTypes && element._eventTypes[eventType]){ 352 var handlers = element._eventTypes[eventType]; 353 for(var i=0; i<handlers.length; i++){ 354 if(handlers[i] === handler){ 355 handlers[i]=null; 356 handlers.splice(i, 1); 357 break; 358 } 359 } 360 } 361 }else{ 362 // J.out('removeEventListener: handler is undefined. \n caller: '+ removeEventListener.caller); 363 //J.out('removeEventListener: handler is undefined. \n element: '+ element + ', eventType:' + eventType); 364 } 365 }else{ 366 367 if(element._eventTypes && element._eventTypes[eventType]){ 368 var handlers = element._eventTypes[eventType]; 369 370 for(var i=0; i<handlers.length; i++){ 371 element.removeEventListener(eventType, handlers[i], false); 372 } 373 element._eventTypes[eventType] = []; 374 } 375 376 } 377 }else{ 378 if(element._eventTypes){ 379 var eventTypes = element._eventTypes; 380 for(var p in eventTypes){ 381 var handlers = element._eventTypes[p]; 382 for(var i=0; i<handlers.length; i++){ 383 element.removeEventListener(p, handlers[i], false); 384 } 385 } 386 eventTypes = {}; 387 } 388 } 389 390 }; 391 } 392 // In IE 5 and later, we use attachEvent( ) and detachEvent( ), with a number of 393 // hacks to make them compatible with addEventListener and removeEventListener. 394 else if (document.attachEvent) {//<del>这里不能用特性判断, 否则opera也会使用这个方法绑定事件</del> 395 //<del>ie都用这个方法是因为ie9对标准的addEventListener支持不完整</del> 396 /** 397 * 兼容ie的写法 398 * @ignore 399 */ 400 addEventListener = function(element, eventType, handler,options) { 401 if(customEvent["on"+eventType]){ 402 customEvent["on"+eventType](element, eventType, handler,options); 403 return; 404 } 405 addOriginalEventListener(element, eventType, handler); 406 }; 407 /** 408 * @ignore 409 */ 410 addOriginalEventListener = function(element, eventType, handler) { 411 if ($E._find(arguments) != -1){ 412 return; 413 } 414 /** 415 * @ignore 416 */ 417 var wrappedEvent = function(e){ 418 419 var event = standardizeEvent(e, element); 420 421 if (Function.prototype.call){ 422 handler.call(element, event); 423 }else { 424 // If we don't have Function.call, fake it like this. 425 element._currentHandler = handler; 426 element._currentHandler(event); 427 element._currentHandler = null; 428 } 429 }; 430 431 // Now register that nested function as our event handler. 432 element.attachEvent("on" + eventType, wrappedEvent); 433 434 435 var h = { 436 element: element, 437 eventType: eventType, 438 handler: handler, 439 wrappedEvent: wrappedEvent 440 }; 441 442 443 var d = element.document || element; 444 // Now get the window associated with that document. 445 var w = d.parentWindow || window; 446 447 // We have to associate this handler with the window, 448 // so we can remove it when the window is unloaded. 449 var id = $E._uid(); // Generate a unique property name 450 if (!w._allHandlers) w._allHandlers = {}; // Create object if needed 451 w._allHandlers[id] = h; // Store the handler info in this object 452 453 // And associate the id of the handler info with this element as well. 454 if (!element._handlers) element._handlers = []; 455 element._handlers.push(id); 456 457 // If there is not an onunload handler associated with the window, 458 // register one now. 459 if (!w._onunloadEventRegistered) { 460 w._onunloadEventRegistered = true; 461 w.attachEvent("onunload", $E._removeAllEvents); 462 } 463 }; 464 465 /** 466 * 兼容ie的写法 467 * @ignore 468 */ 469 removeEventListener = function(element, eventType, handler) { 470 if(customEvent["off"+eventType]){ 471 customEvent["off"+eventType](element, eventType,handler); 472 return; 473 } 474 if(arguments.length == 3){ 475 removeOriginalEventListener(element, eventType, handler); 476 }else{ 477 removeOriginalEventListener(element, eventType); 478 } 479 }; 480 /** 481 * @ignore 482 */ 483 removeOriginalEventListener = function(element, eventType, handler) { 484 // Find this handler in the element._handlers[] array. 485 var handlersIndex = $E._find(arguments); 486 if (handlersIndex == -1) return; // If the handler was not registered, do nothing 487 // Get the window of this element. 488 var d = element.document || element; 489 var w = d.parentWindow || window; 490 for(var j=0; j<handlersIndex.length; j++){ 491 var i = handlersIndex[j]; 492 // Look up the unique id of this handler. 493 var handlerId = element._handlers[i]; 494 // And use that to look up the handler info. 495 var h = w._allHandlers[handlerId]; 496 // Using that info, we can detach the handler from the element. 497 element.detachEvent("on" + h.eventType, h.wrappedEvent); 498 // Remove one element from the element._handlers array. 499 element._handlers[i]=null; 500 element._handlers.splice(i, 1); 501 // And delete the handler info from the per-window _allHandlers object. 502 delete w._allHandlers[handlerId]; 503 } 504 if(element._handlers && element._handlers.length==0){ 505 element._handlers=null; 506 } 507 }; 508 509 // A utility function to find a handler in the element._handlers array 510 // Returns an array index or -1 if no matching handler is found 511 $E._find = function(args) { 512 var element = args[0], 513 eventType = args[1], 514 handler = args[2], 515 handlers = element._handlers; 516 517 if (!handlers){ 518 return -1; // if no handlers registered, nothing found 519 } 520 521 // Get the window of this element 522 var d = element.document || element; 523 var w = d.parentWindow || window; 524 525 var handlersIndex = []; 526 527 if(args.length === 3){ 528 // Loop through the handlers associated with this element, looking 529 // for one with the right type and function. 530 // We loop backward because the most recently registered handler 531 // is most likely to be the first removed one. 532 for(var i = handlers.length-1; i >= 0; i--) { 533 var handlerId = handlers[i]; // get handler id 534 var h = w._allHandlers[handlerId]; // get handler info 535 // If handler info matches type and handler function, we found it. 536 if (h.eventType == eventType && h.handler == handler){ 537 handlersIndex.push(i); 538 return handlersIndex; 539 } 540 } 541 }else if(args.length === 2){ 542 543 for(var i = handlers.length-1; i >= 0; i--) { 544 var handlerId = handlers[i]; // get handler id 545 var h = w._allHandlers[handlerId]; // get handler info 546 // If handler info matches type and handler function, we found it. 547 if (h.eventType == eventType){ 548 handlersIndex.push(i); 549 } 550 } 551 if(handlersIndex.length>0){ 552 return handlersIndex; 553 } 554 555 }else if(args.length === 1){ 556 557 for(var i = handlers.length-1; i >= 0; i--) { 558 handlersIndex.push(i); 559 } 560 if(handlersIndex.length>0){ 561 return handlersIndex; 562 } 563 } 564 565 566 567 568 569 570 return -1; // No match found 571 }; 572 573 $E._removeAllEvents = function( ) { 574 // This function is registered as the onunload handler with 575 // attachEvent. This means that the this keyword refers to the 576 // window in which the event occurred. 577 var id, 578 w = this; 579 580 // Iterate through all registered handlers 581 for(id in w._allHandlers) { 582 // It would throw a refused access error 583 // so I catch it. by azrael. 584 // try{ 585 // J.out('RemoveEvent: ' + id, 'RemoveAllEvents'); 586 // Get handler info for this handler id 587 var h = w._allHandlers[id]; 588 // Use the info to detach the handler 589 h.element.detachEvent("on" + h.eventType, h.wrappedEvent); 590 h.element._handlers=null; 591 // Delete the handler info from the window 592 delete w._allHandlers[id]; 593 // }catch(e){ 594 // J.out('RemoveEventError: ' + e, 'RemoveAllEvents'); 595 // } 596 } 597 } 598 599 // Private utility to generate unique handler ids 600 $E._counter = 0; 601 $E._uid = function(){ 602 return "h" + $E._counter++; 603 }; 604 } 605 customEvent = { 606 "ondrag" : function(element, eventType, handler){ 607 var _oldX, 608 _oldY, 609 isMove=false, 610 orientMousedownEvent; 611 var onElMousedown = function(e){ 612 if(!J.browser.mobileSafari && e.button !== 0){//非左键点击直接return 613 return; 614 } 615 var touch; 616 orientMousedownEvent = e; 617 if(J.browser.mobileSafari){ 618 //console.info("touchstart"); 619 e.stopPropagation(); 620 touch = e.touches[0]; 621 _oldX= touch.pageX; 622 _oldY= touch.pageY; 623 }else{ 624 //TODO 这里阻止了事件冒泡,可能会有问题 625 e.stopPropagation(); 626 e.preventDefault(); 627 _oldX= e.clientX; 628 _oldY= e.clientY; 629 } 630 isMove=false; 631 if(J.browser.mobileSafari){ 632 $E.addEventListener(document, "touchmove", onElMousemove); 633 $E.addEventListener(element, "touchend", onElMouseup); 634 }else{ 635 $E.addEventListener(document, "mousemove", onElMousemove); 636 } 637 // J.out('onElMousedown: '+e.target.id ); 638 }; 639 var onElMousemove = function(e){ 640 if(!J.browser.mobileSafari && e.button !== 0){//非左键点击直接return 641 return; 642 } 643 var x,y,touch; 644 e.stopPropagation(); 645 if(J.browser.mobileSafari){ 646 //console.info("touchmove"); 647 touch = e.changedTouches[0]; 648 x= touch.pageX; 649 y= touch.pageY; 650 }else{ 651 x = e.clientX; 652 y = e.clientY; 653 } 654 if(Math.abs(_oldX-x)+Math.abs(_oldY-y)>2) { 655 // J.out("customdrag"); 656 //console.info("customdrag"); 657 if(J.browser.mobileSafari){ 658 $E.removeEventListener(document, "touchmove", onElMousemove); 659 $E.removeEventListener(element, "touchend", onElMouseup); 660 }else{ 661 $E.removeEventListener(document, "mousemove", onElMousemove); 662 } 663 if(!isMove){ 664 handler.call(element,e); 665 isMove=true; 666 } 667 }else{ 668 //console.info( Math.abs(_oldX-x)+Math.abs(_oldY-y)>2 ) 669 } 670 }; 671 var onElMouseup = function(e){ 672 if(!J.browser.mobileSafari && e.button !== 0){//非左键点击直接return 673 return; 674 } 675 /* 676 var x,y,touch; 677 if(J.browser.mobileSafari){ 678 touch = e.touches[0]; 679 _oldX= touch.pageX; 680 _oldY= touch.pageY; 681 }else{ 682 x = e.clientX; 683 y = e.clientY; 684 } 685 if(Math.abs(_oldX-x)+Math.abs(_oldY-y)<2) { 686 isMove=false; 687 if(J.browser.mobileSafari){ 688 $E.removeEventListener(document, "touchmove", onElMousemove); 689 }else{ 690 $E.removeEventListener(document, "mousemove", onElMousemove); 691 } 692 }else{ 693 694 } 695 */ 696 //console.info("touch end"); 697 if(J.browser.mobileSafari){ 698 $E.removeEventListener(document, "touchmove", onElMousemove); 699 if(!isMove){ 700 //console.info("not draging"); 701 /* 702 var point = e.changedTouches[0]; 703 target = document.elementFromPoint(point.pageX,point.pageY); 704 if(target.tagName=="IFRAME"){ 705 return; 706 }else{ 707 } 708 // Create the fake event 709 ev = document.createEvent('MouseEvents'); 710 ev.initMouseEvent('click', true, true, e.view, 1, 711 point.screenX, point.screenY, point.clientX, point.clientY, 712 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 713 0, null); 714 ev._fake = true; 715 target.dispatchEvent(ev); 716 */ 717 }else{ 718 e.stopPropagation(); 719 e.preventDefault(); 720 //console.info("is draging"); 721 } 722 }else{ 723 $E.removeEventListener(document, "mousemove", onElMousemove); 724 //if(!isMove){ 725 //@TODO fire the event 726 //} 727 } 728 }; 729 if(J.browser.mobileSafari){ 730 $E.addEventListener(element, "touchstart", onElMousedown); 731 }else{ 732 $E.addEventListener(element, "mousedown", onElMousedown); 733 $E.addEventListener(element, "mouseup", onElMouseup); 734 } 735 // J.out('element: ' + element.id); 736 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler,"actions":[onElMousedown,onElMouseup]}); 737 }, 738 "offdrag" : function(element, eventType,handler){ 739 for(var i in customEventHandlers){ 740 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 741 if(J.browser.mobileSafari){ 742 $E.removeEventListener(element, "touchstart",customEventHandlers[i].actions[0]); 743 $E.removeEventListener(element, "touchend",customEventHandlers[i].actions[1]); 744 }else{ 745 $E.removeEventListener(element, "mousedown",customEventHandlers[i].actions[0]); 746 $E.removeEventListener(element, "mouseup",customEventHandlers[i].actions[1]); 747 } 748 customEventHandlers.splice(i,1); 749 break; 750 } 751 } 752 }, 753 "oncustomclick" : function(element, eventType, handler, options){//@ longTouchable 是否触发长按事件 add by ip 754 var _oldX, 755 _oldY, 756 isMove=false, 757 isClicked = false, 758 timeStamp=0, 759 longTouchTimer, 760 options= options?options:{}, 761 longtouchable = options.longtouchable, 762 mouseButton = -1; 763 var onElMousedown = function(e){ 764 // console.log('1: ' + e.button); 765 timeStamp = e.timeStamp; 766 isMove = false; 767 if(!J.browser.mobileSafari && e.button !== 0){//非左键点击直接return 768 return; 769 } 770 var touch; 771 if(J.browser.mobileSafari){ 772 touch = e.changedTouches[0]; 773 _oldX = touch.pageX; 774 _oldY = touch.pageY; 775 }else{ 776 _oldX = e.clientX; 777 _oldY = e.clientY; 778 } 779 isClicked = false; 780 if(longtouchable){ 781 longTouchTimer = setTimeout(function(){ 782 if(isMove || isClicked){ 783 return; 784 } 785 var clickTime = 2000;//TODO (new Date()).getTime() - timeStamp; 786 //console.info('setTimeout',clickTime); 787 if(J.browser.mobileSafari){ 788 $E.removeEventListener(element, "touchmove",onElMouseMove); 789 $E.removeOriginalEventListener(element, "touchend",onElClick); 790 }else{ 791 $E.removeEventListener(element, "mousemove",onElMouseMove); 792 $E.removeOriginalEventListener(element, "click",onElClick); 793 } 794 handler.call(element,e,clickTime); 795 },1000); 796 } 797 if(J.browser.mobileSafari){ 798 $E.addEventListener(element, "touchmove", onElMouseMove); 799 $E.addOriginalEventListener(element, "touchend", onElClick); 800 }else{ 801 $E.addEventListener(element, "mousemove", onElMouseMove); 802 $E.addOriginalEventListener(element, "click", onElClick); 803 } 804 // e.preventDefault(); 805 // e.stopPropagation(); 806 }; 807 var onElMouseup = function(e){ 808 mouseButton = e.button; 809 // console.log('2: ' + e.button); 810 if(!J.browser.mobileSafari && e.button !== 0){//非左键点击直接return 811 return; 812 } 813 if(J.browser.mobileSafari){ 814 touch = e.changedTouches[0]; 815 var x = touch.pageX; 816 var y = touch.pageY; 817 } 818 }; 819 var onElMouseMove = function(e){ 820 if(J.browser.mobileSafari){ 821 touch = e.changedTouches[0]; 822 var x = touch.pageX; 823 var y = touch.pageY; 824 }else{ 825 var x = e.clientX; 826 var y = e.clientY; 827 } 828 isMove = Math.abs(_oldX-x)+Math.abs(_oldY-y) > 1; 829 if(isMove){ 830 clearTimeout(longTouchTimer); 831 longTouchTimer = null; 832 if(J.browser.mobileSafari){ 833 $E.removeEventListener(element, "touchmove",onElMouseMove); 834 $E.removeOriginalEventListener(element, "touchend",onElClick); 835 }else{ 836 $E.removeEventListener(element, "mousemove",onElMouseMove); 837 $E.removeOriginalEventListener(element, "click",onElClick); 838 } 839 } 840 } 841 var onElClick = function(e){ 842 //console.info('clicked'); 843 clearTimeout(longTouchTimer); 844 longTouchTimer = null; 845 isClicked = true; 846 if(!J.browser.mobileSafari && mouseButton !== 0){//非左键点击直接return 847 return; 848 } 849 var touch; 850 var clickTime = 0;//e.timeStamp - timeStamp; 851 if(J.browser.mobileSafari){ 852 touch = e.changedTouches[0]; 853 var x = touch.pageX; 854 var y = touch.pageY; 855 }else{ 856 var x = e.clientX; 857 var y = e.clientY; 858 } 859 if(Math.abs(_oldX-x)+Math.abs(_oldY-y)<1) { 860 isMove=false; 861 // J.out("this is a customclick","click"); 862 //console.info('customclick'); 863 // if(eventType=="click"){ 864 handler.call(element,e,clickTime); 865 // } 866 }else{ 867 //console.info('not customclick'); 868 } 869 }; 870 if(J.browser.mobileSafari){ 871 $E.addEventListener(element, "touchstart", onElMousedown); 872 }else{ 873 $E.addEventListener(element, "mousedown", onElMousedown); 874 $E.addEventListener(element, "mouseup", onElMouseup); 875 } 876 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler,"actions":[onElMousedown,onElMouseMove,onElMouseup,onElClick]}); 877 }, 878 "offcustomclick" : function(element, eventType,handler){ 879 for(var i in customEventHandlers){ 880 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 881 if(J.browser.mobileSafari){ 882 $E.removeEventListener(element, "touchstart",customEventHandlers[i].actions[0]); 883 $E.removeEventListener(element, "touchmove",customEventHandlers[i].actions[1]); 884 $E.removeOriginalEventListener(element, "touchend",customEventHandlers[i].actions[3]); 885 }else{ 886 $E.removeEventListener(element, "mousedown",customEventHandlers[i].actions[0]); 887 $E.removeEventListener(element, "mousemove",customEventHandlers[i].actions[1]); 888 $E.removeEventListener(element, "mouseup",customEventHandlers[i].actions[2]); 889 $E.removeOriginalEventListener(element, "click",customEventHandlers[i].actions[3]); 890 } 891 customEventHandlers.splice(i,1); 892 break; 893 } 894 } 895 }, 896 "oncontextmenu" : function(element, eventType, handler){ 897 if(J.browser.ie == 9){ 898 /** 899 * @ignore 900 */ 901 var wrappedEvent = function(e){ 902 var event = standardizeEvent(e, element); 903 handler.call(element, event); 904 }; 905 element.attachEvent('oncontextmenu', wrappedEvent); 906 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler,"actions":[wrappedEvent]}); 907 }else{ 908 $E.addOriginalEventListener(element, eventType, handler); 909 } 910 }, 911 "offcontextmenu" : function(element, eventType, handler){ 912 if(J.browser.ie == 9){ 913 for(var i in customEventHandlers){ 914 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 915 element.detachEvent('oncontextmenu', customEventHandlers[i].actions[0]); 916 customEventHandlers.splice(i,1); 917 break; 918 } 919 } 920 }else{ 921 $E.removeOriginalEventListener(element, eventType, handler); 922 } 923 }, 924 "onmousewheel" : function(element, eventType, handler){ 925 if(J.browser.firefox){ 926 var wrappedEvent = function(e){ 927 e.wheelDelta = -40*e.detail; 928 handler.call(element, e); 929 }; 930 $E.addOriginalEventListener(element, "DOMMouseScroll", wrappedEvent); 931 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler,"actions":[wrappedEvent]}); 932 }else{ 933 $E.addOriginalEventListener(element, "mousewheel" , handler); 934 } 935 }, 936 "offmousewheel" : function(element, eventType, handler){ 937 if(J.browser.firefox){ 938 for(var i in customEventHandlers){ 939 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 940 $E.removeOriginalEventListener(element, "DOMMouseScroll", customEventHandlers[i].actions[0]); 941 customEventHandlers.splice(i,1); 942 break; 943 } 944 } 945 }else{ 946 $E.removeOriginalEventListener(element, "mousewheel", handler); 947 } 948 }, 949 "onmouseenter" : function(element, eventType, handler){ 950 var onElMouseEnter = function(e){ 951 var s = e.relatedTarget; 952 if(!s){//relatedTarget为null, 鼠标浏览器外移进来 953 handler.call(this, e); 954 }else if(this.compareDocumentPosition){//非ie 955 var res = this.compareDocumentPosition(s); 956 if(!(s == this || res == 20 || res == 0)){ 957 handler.call(this, e); 958 } 959 }else{ 960 if(!(s == this || this.contains(s))){ 961 handler.call(this, e); 962 } 963 } 964 }; 965 966 $E.addEventListener(element, "mouseover", onElMouseEnter); 967 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler, actions: [onElMouseEnter]}); 968 }, 969 "offmouseenter" : function(element, eventType,handler){ 970 for(var i in customEventHandlers){ 971 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 972 $E.removeEventListener(element, "mouseover",customEventHandlers[i].actions[0]); 973 customEventHandlers.splice(i, 1); 974 break; 975 } 976 } 977 }, 978 "onmouseleave" : function(element, eventType, handler){ 979 var onElMouseLeave = function(e){ 980 var s = e.relatedTarget; 981 if(!s){//relatedTarget为null, 鼠标移到浏览器外了 982 handler.call(this, e); 983 }else if(this.compareDocumentPosition){//非ie 984 var res = this.compareDocumentPosition(s); 985 if(!(res == 20 || res == 0)){ 986 handler.call(this, e); 987 } 988 }else{ 989 if(!this.contains(s)){ 990 handler.call(this, e); 991 } 992 } 993 }; 994 $E.addEventListener(element, "mouseout", onElMouseLeave); 995 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler, actions: [onElMouseLeave]}); 996 }, 997 "offmouseleave" : function(element, eventType,handler){ 998 for(var i in customEventHandlers){ 999 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 1000 $E.removeEventListener(element, "mouseout",customEventHandlers[i].actions[0]); 1001 customEventHandlers.splice(i, 1); 1002 break; 1003 } 1004 } 1005 }, 1006 "oninput" : function(element, eventType, handler){ 1007 if(J.browser.ie){ 1008 /** 1009 * @ignore 1010 */ 1011 var wrappedEvent = function(e){ 1012 if(e.propertyName.toLowerCase() == "value"){ 1013 var event = standardizeEvent(e, element); 1014 handler.call(element, event); 1015 } 1016 }; 1017 element.attachEvent("onpropertychange", wrappedEvent); 1018 customEventHandlers.push({"element":element,"eventType":eventType,handler:handler,"actions":[wrappedEvent]}); 1019 if(J.browser.ie==9){ //fix ie9 bug, can not fire when characters are deleted 1020 $E.addOriginalEventListener(element, "change" , handler); 1021 } 1022 }else{ 1023 $E.addOriginalEventListener(element, "input" , handler); 1024 } 1025 }, 1026 "offinput" : function(element, eventType, handler){ 1027 if(J.browser.ie){ 1028 for(var i in customEventHandlers){ 1029 if(customEventHandlers[i].handler==handler&&customEventHandlers[i].element==element&&customEventHandlers[i].eventType==eventType){ 1030 element.detachEvent("onpropertychange", customEventHandlers[i].actions[0]); 1031 customEventHandlers.splice(i,1); 1032 break; 1033 } 1034 } 1035 if(J.browser.ie==9){ 1036 $E.removeOriginalEventListener(element, "change" , handler); 1037 } 1038 }else{ 1039 $E.removeOriginalEventListener(element, "input", handler); 1040 } 1041 } 1042 1043 } 1044 1045 1046 1047 1048 1049 1050 1051 1052 /** 1053 * 1054 * 文档加载完成时事件监听器 1055 * 1056 * @method onDomReady 1057 * @memberOf event 1058 * 1059 * @param element 元素 1060 * @param eventType 事件类型,不含on 1061 * @param handler 事件处理器 1062 */ 1063 onDomReady = function( f ) { 1064 // If the DOM is already loaded, execute the function right away 1065 if ( onDomReady.done ) { 1066 return f(); 1067 } 1068 1069 // If we’ve already added a function 1070 if ( onDomReady.timer ) { 1071 // Add it to the list of functions to execute 1072 onDomReady.ready.push( f ); 1073 } else { 1074 // 初始化onDomReady后要执行的function的数组 1075 onDomReady.ready = [ f ]; 1076 1077 // Attach an event for when the page finishes loading, 1078 // just in case it finishes first. Uses addEvent. 1079 $E.on(window, "load", isDomReady); 1080 1081 // Check to see if the DOM is ready as quickly as possible 1082 onDomReady.timer = window.setInterval( isDomReady, 300 ); 1083 } 1084 } 1085 1086 /** 1087 * 1088 * 判断文档加载是否完成 1089 * 1090 * @method isDomReady 1091 * @memberOf event 1092 * 1093 * @param element 元素 1094 * @param eventType 事件类型,不含on 1095 * @param handler 事件处理器 1096 */ 1097 // Checks to see if the DOM is ready for navigation 1098 isDomReady = function() { 1099 // If we already figured out that the page is ready, ignore 1100 if ( onDomReady.done ) { 1101 return true; 1102 } 1103 1104 // Check to see if a number of functions and elements are 1105 // able to be accessed 1106 if ( document && document.getElementsByTagName && document.getElementById && document.body ) { 1107 // Remember that we’re now done 1108 onDomReady.done = true; 1109 1110 // If they’re ready, we can stop checking 1111 window.clearInterval( onDomReady.timer ); 1112 onDomReady.timer = null; 1113 1114 // Execute all the functions that were waiting 1115 for ( var i = 0; i < onDomReady.ready.length; i++ ){ 1116 onDomReady.ready[i](); 1117 } 1118 1119 onDomReady.ready = null; 1120 1121 return true; 1122 } 1123 } 1124 1125 1126 1127 1128 /** 1129 * 创建一个消息源发布者的类 1130 * Publish 1131 * @class 1132 * @return {Object} 返回生成的消息源 1133 * @memberOf event 1134 * @constructor 1135 * @example 1136 * Jx().$package(function(J){ 1137 * var onMsg = new J.Publish(); 1138 * var funcA = function(option){ 1139 * alert(option); 1140 * }; 1141 * // 注册一个事件的观察者 1142 * onMsg.subscribe(funcA); 1143 * var option = "demo"; 1144 * onMsg.deliver(option); 1145 * onMsg.unsubscribe(funcA); 1146 * onMsg.deliver(option); 1147 * 1148 * }; 1149 * 1150 */ 1151 Publish = function(){ 1152 this.subscribers = []; 1153 }; 1154 1155 /** 1156 * 注册观察者 1157 * @memberOf event.Publish.prototype 1158 * @function 1159 * @param {Function} func 要注册的观察者 1160 * @return {Function} 返回结果 1161 */ 1162 Publish.prototype.subscribe = function(func){ 1163 var alreadyExists = J.array.some(this.subscribers, function(el){ 1164 return el === func; 1165 }); 1166 if(!alreadyExists){ 1167 this.subscribers.push(func); 1168 } 1169 return func; 1170 }; 1171 1172 /** 1173 * 触发事件 1174 * @memberOf event.Publish.prototype 1175 * @param {Mixed} msg 要注册的观察者 1176 * @return {Function} 返回结果 1177 */ 1178 Publish.prototype.deliver = function(msg){ 1179 J.array.forEach(this.subscribers, function(fn){ 1180 fn(msg); 1181 }); 1182 }; 1183 1184 /** 1185 * 注销观察者 1186 * @memberOf event.Publish.prototype 1187 * @param {Function} func 要注销的观察者 1188 * @return {Function} 返回结果 1189 */ 1190 Publish.prototype.unsubscribe = function(func){ 1191 this.subscribers = J.array.filter(this.subscribers, function(el){ 1192 return el !== func; 1193 }); 1194 return func; 1195 }; 1196 1197 1198 1199 1200 1201 1202 1203 1204 /** 1205 * 1206 * 为自定义Model添加事件监听器 1207 * 1208 * @method addObserver 1209 * @memberOf event 1210 * 1211 * @param targetModel 目标 model,即被观察的目标 1212 * @param eventType 事件类型,不含on 1213 * @param handler 观察者要注册的事件处理器 1214 */ 1215 addObserver = function(targetModel, eventType, handler){ 1216 var handlers, 1217 length, 1218 index, 1219 i; 1220 if(handler){ 1221 1222 1223 // 转换成完整的事件描述字符串 1224 eventType = "on" + eventType; 1225 1226 // 判断对象是否含有$events对象 1227 if(!targetModel._$events){ 1228 targetModel._$events={}; 1229 } 1230 1231 // 判断对象的$events对象是否含有eventType描述的事件类型 1232 if(!targetModel._$events[eventType]){ 1233 //若没有则新建 1234 targetModel._$events[eventType] = []; 1235 }else if(targetModel._$events[eventType].length == 0){ 1236 //bug: ie会有引用泄漏的问题, 如果数组为空, 则重新创建一个 1237 targetModel._$events[eventType] = []; 1238 } 1239 1240 handlers = targetModel._$events[eventType]; 1241 length = handlers.length; 1242 index = -1; 1243 1244 // 通过循环,判断对象的handlers数组是否已经含有要添加的handler 1245 for(i=0; i<length; i++){ 1246 1247 var tempHandler = handlers[i]; 1248 1249 if(tempHandler == handler){ 1250 index = i; 1251 break; 1252 } 1253 } 1254 // 如果没有找到,则加入此handler 1255 if(index === -1){ 1256 handlers.push(handler); 1257 //alert(handlers[handlers.length-1]) 1258 } 1259 }else{ 1260 J.out(">>> 添加的观察者方法不存在:"+targetModel+eventType+handler); 1261 } 1262 }; 1263 /** 1264 * 1265 * 批量为自定义Model添加事件监听器 1266 * 1267 * @method addObservers 1268 * @memberOf event 1269 * 1270 * @param obj 目标 model,即被观察的目标 1271 * obj = { targetModel : {eventType:handler,eventType2:handler2...} , targetModel2: {eventType:handler,eventType2:handler2...} } 1272 */ 1273 addObservers = function(obj){ 1274 //TODO 这里的代码是直接复制addObserver的(为避免太多函数调用耗费栈) 1275 var t=obj['targetModel']; 1276 var m=obj['eventMapping']; 1277 for(var i in m){ 1278 addObserver(t,i,m[i]); 1279 } 1280 1281 }; 1282 /** 1283 * 1284 * 触发自定义Model事件的监听器 1285 * 1286 * @method notifyObservers 1287 * @memberOf event 1288 * 1289 * @param targetModel 目标 model,即被观察目标 1290 * @param eventType 事件类型,不含on 1291 * @param options 触发的参数对象 1292 * @return {Boolean} 1293 */ 1294 notifyObservers = function(targetModel, eventType, argument){/*addInvokeTime(eventType);*/ 1295 var handlers, 1296 i; 1297 1298 eventType = "on" + eventType; 1299 var flag = true; 1300 if(targetModel._$events && targetModel._$events[eventType]){ 1301 handlers = targetModel._$events[eventType]; 1302 if(handlers.length > 0){ 1303 // 通过循环,执行handlers数组所包含的所有函数function 1304 for(i=0; i<handlers.length; i++){ 1305 if(handlers[i].apply(targetModel, [argument]) === false){ 1306 flag = false; 1307 } 1308 } 1309 //return flag; 1310 } 1311 }else{ 1312 // throw new Error("还没有定义 [" + targetModel + "] 对象的: " + eventType + " 事件!"); 1313 //return false; 1314 } 1315 return flag; 1316 }; 1317 1318 1319 /** 1320 * 1321 * 为自定义 Model 移除事件监听器 1322 * 1323 * @method removeObserver 1324 * @memberOf event 1325 * 1326 * @param targetModel 目标 model,即被观察的目标 1327 * @param eventType 事件类型,不含on 1328 * @param handler 观察者要取消注册的事件处理器 1329 */ 1330 // 按照对象和事件处理函数来移除事件处理函数 1331 removeObserver = function(targetModel, eventType, handler){ 1332 var i, 1333 j, 1334 flag = false, 1335 handlers, 1336 length, 1337 events = targetModel._$events; 1338 if(handler){ 1339 1340 if(events){ 1341 eventType = "on" + eventType; 1342 handlers = events[eventType]; 1343 1344 if(handlers){ 1345 length = handlers.length; 1346 for(i=0; i<length; i++){ 1347 //由==修改为=== 1348 if(handlers[i] == handler){ 1349 handlers[i] = null; 1350 handlers.splice(i, 1); 1351 flag = true; 1352 break; 1353 } 1354 } 1355 } 1356 1357 1358 } 1359 }else if(eventType){ 1360 if(events){ 1361 eventType = "on" + eventType; 1362 handlers = events[eventType]; 1363 if(handlers){ 1364 length = handlers.length; 1365 for(i=0; i<length; i++){ 1366 handlers[i] = null; 1367 } 1368 delete events[eventType]; 1369 flag = true; 1370 } 1371 1372 } 1373 1374 }else if(targetModel){ 1375 if(events){ 1376 for(i in events){ 1377 delete events[i]; 1378 } 1379 delete targetModel._$events; 1380 flag = true; 1381 } 1382 } 1383 return flag; 1384 }; 1385 1386 $E.addEventListener = addEventListener; 1387 $E.removeEventListener = removeEventListener; 1388 $E.addOriginalEventListener = addOriginalEventListener; 1389 $E.removeOriginalEventListener = removeOriginalEventListener; 1390 // alias 1391 $E.on = $E.addEventListener; 1392 $E.off = $E.removeEventListener; 1393 1394 $E.onDomReady = onDomReady; 1395 1396 $E.Publish = Publish; 1397 1398 // Model 事件方法 1399 $E.addObserver = addObserver; 1400 $E.addObservers = addObservers; 1401 $E.notifyObservers = notifyObservers; 1402 $E.removeObserver = removeObserver; 1403 }); 1404