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