OSDN Git Service

7ff83b395829e665c588d390456f0cd0d726b519
[pettanr/clientJs.git] / 0.6.x / js / 01_core / 13_XEventDispatcher.js
1 /**\r
2  * X.EventDispatcher\r
3  * \r
4  *  1. as3 の EventDispatcher ライクなクラス。そのまま使ったり、継承したり。コールバック中にイベントを追加したら?削除したら?にも対処している。\r
5  *  2. _rawObject メンバがいる場合、addEventListener, attachEvent, on 等で生のブラウザオブジェクトにリスナを登録する。\r
6  *     window, document, HTMLElement, Image, XHR などが _rawObject\r
7  * \r
8  * use X.Callback\r
9  * \r
10  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.addEventListener\r
11  * イベント発送中のリスナーの追加\r
12  * EventListener がイベント処理中に EventTarget に追加された場合、それが現在のアクションによって実行されることはありませんが、浮上フェーズのように、後の段階のイベントフローで実行されるかもしれません。\r
13  * \r
14  * https://developer.mozilla.org/ja/docs/Web/API/EventTarget.removeEventListener\r
15  * イベントリスナーが イベントを処理中であるイベントターゲットから削除された場合、現在のアクションによってそのイベントリスナーが実行されることはありません。\r
16  * イベントリスナーは、決して削除された後に実行されることはありません。\r
17  * イベントターゲット上にある現在のどのイベントリスナーも指定していない引数付きの removeEventListener は、何の効果もありません。\r
18  */\r
19 \r
20 // ------------------------------------------------------------------------- //\r
21 // ------------ local variables -------------------------------------------- //\r
22 // ------------------------------------------------------------------------- //\r
23 var X_EventDispatcher_once       = false,\r
24         X_EventDispatcher_lock       = false,\r
25         X_EventDispatcher_unlock     = false,\r
26         X_EventDispatcher_needsIndex = false,\r
27         \r
28         X_EventDispatcher_safariPreventDefault = false; // Safari3-\r
29 \r
30 // ------------------------------------------------------------------------- //\r
31 // --- interface ----------------------------------------------------------- //\r
32 // ------------------------------------------------------------------------- //\r
33 \r
34 /**\r
35  * イベントターゲット(widnow, document, Image, XHR, Silverlight 等)をラップする場合、通常は new 時に渡します。参照:コンストラクタ実体 {@link X.EventDispatcher.Constructor}\r
36  * アプリケーション独自のイベントをやり取りしたいだけ、という場合、イベントターゲットは不要です。\r
37  * @class\r
38  * @classdesc EventTarget オブジェクトをラップしたり、アプリケーションで独自に定義したイベントを発信するためのクラスです。\r
39  *     listen, unlisten, dispatch という addEventListener, removeEventListener, dispatchEvent に対応する関数を持ちます。\r
40  *     さらに listening という as3 の hasEventListener に相当する関数を持ちます。\r
41  *     イベントターゲットオブジェクト(widnow, document, HTMLElement, XHR 等)が _rawObject に設定されていた場合に、それらへ実際のイベント登録・解除も行います。\r
42  *     このイベントの登録・解除はクロスブラウザで、IE5~8 の独自イベントの差異を吸収し、DOM0 に対しても複数のコールバックを登録することができます。\r
43  *     またコールバックに対して、this コンテキストや、追加の引数を指定もできます。 this コンテキストを指定しなかった場合、EventDispatcher インスタンスがコールバックの this になります。 \r
44  *     unlisten() は、引数を指定しなかった場合、全てのイベントを解除します。ただし、systemListen 経由で登録されたハンドラは解除されません。\r
45  *     systemListen, systemUnlisten は、ライブラリ内のコードからしかアクセスできません。\r
46  * @param {object=} opt_rawObject\r
47  */\r
48 X.EventDispatcher =\r
49         X.Class.create(\r
50                 'EventDispatcher',\r
51                 \r
52         /** @lends {X.EventDispatcher.prototype} */\r
53                 {\r
54 \r
55             /**\r
56              * @namespace\r
57              * @memberof X.EventDispatcher\r
58              */\r
59 \r
60                 // TODO _rawObjectType EventTarget, XHR, Silverlight, ...\r
61                 /**\r
62                  * イベントリスナをイベント名(string)や数値(1~,フレームワーク内で定義)をキーとするArrayで記憶します。\r
63                  * Arrayには、{k:種類,x:コンテキスト(thisObject),f:関数,s:サプリメントする引数の配列} というハッシュ、または関数が蓄えられています。\r
64                  * @private\r
65                  * @type {Object.<(number|string), Array.<(callbackHash|function)>>}\r
66                  */\r
67                         '_listeners'    : null,\r
68 \r
69                 /**\r
70                  * _rawObject には HTMLElement, window, document, XHR といったイベントターゲットオブジェクトを設定します。\r
71                  * _rawObject が設定されていると on(), off() 時に addEventListener(DOM Level2) や detachEvent(ie5~8), on~(DOM0) 等を操作します。\r
72                  * _rawObject は最初の on() 前に設定しておかないと addEventListener 等が意図したように行われません。\r
73                  * X.Node では非同期に HTMLElement を生成していて、要素生成以前に on, off を呼び出すことができます。これは適宜に migrateEvent, restoreEvent を呼んで解決しているためです。\r
74                  * @private\r
75                  * @type {Object}\r
76                  */\r
77                         '_rawObject'    : null,\r
78                 \r
79                 /**\r
80                  * _rawObject がある場合、イベントターゲットオブジェクトに対して addEventListener, attachEvent, onXX 時に実際に渡される関数。\r
81                  * イベントタイプが変わっても、単一の オブジェクトを使いまわす。(但し例外は、IE8以下の XHR|MSXML に対して。)最初の listen で生成されて、最後の unlisten で削除される。\r
82                  * @private\r
83                  * @type {X.Callback}\r
84                  */\r
85                         '_handleEvent'  : null,\r
86                         \r
87                 /*\r
88                  * dispatch 中に dispatch が呼ばれた際に、そのネストの深さを保存する。\r
89                  * dispatch() 終了時に _dispatching が 0 の場合に、現在のインスタンスの dispatch がすべて終わったことになる。\r
90                  * @private\r
91                  * @type {number}\r
92                  */\r
93                         '_dispatching'  : 0,     // dispatch 中の unlisten で使用\r
94 \r
95                 /*\r
96                  * dispatch 中に listen が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。\r
97                  * _dispatching が 0 のときに _reserves を使って listen() を呼び出す。\r
98                  * @private\r
99                  * @type {Object.<(number|string), Array.<(X.Callback|function)>>}\r
100                  */\r
101                         '_reserves'     : null,\r
102                 \r
103                 /*\r
104                  * dispatch 中に unlisten が呼ばれた場合に、配列のindexがずれることを避けるため、一旦保持する。\r
105                  * _dispatching が 0 のときに _unlistens を使って unlisten() を呼び出す。\r
106                  * @private\r
107                  * @type {Object.<(number|string), Array.<(X.Callback|function)>>}\r
108                  */\r
109                         '_unlistens'    : null,\r
110                         \r
111                 /*\r
112                  * dispatch 中に kill が呼ばれた場合に、X.EventDispatcher インスタンスの削除を dispatch 後にずらすために立てるフラグ。\r
113                  * @private\r
114                  * @type {boolean}\r
115                  */\r
116                         '_killReserved' : false,\r
117                         \r
118             /**\r
119              * X.EventDispatcher のコンストラクタの実体。\r
120              * @constructs\r
121              * @this {X.EventDispatcher}\r
122              * @params {object=} opt_rawObject\r
123              */\r
124                         Constructor : function( opt_rawObject ){\r
125                                 if( opt_rawObject ){\r
126                                         this._rawObject = opt_rawObject;\r
127                                 };\r
128                         },\r
129 \r
130                 /**\r
131                  * 登録されたイベントリスナを呼び出す。イベントリスナの返り値(数値)を OR したものを返す。\r
132                  * @this {X.EventDispatcher}\r
133                  * @return {number}\r
134                  * @param {(eventHash|string|number)} e\r
135                  */     \r
136                         dispatch : X_EventDispatcher_dispatch,\r
137 \r
138             /**\r
139              * \r
140              * @this {X.EventDispatcher}\r
141              */\r
142                         on     : X_EventDispatcher_listen,\r
143                         listen : X_EventDispatcher_listen,\r
144                 \r
145                 /**\r
146                  * 一度 dispatch された時に自動で unlisten されるフラグを立てて listen する。\r
147                  * @this {X.EventDispatcher}\r
148                  * @return {X.EventDispatcher}\r
149                  * @param {(string|number|Array.<(string,number)>)} type\r
150                  * @param {(listener|function|Array)=} opt_arg1\r
151                  * @param {(function|Array=} opt_arg2\r
152                  * @param {Array=} opt_arg3\r
153                  */\r
154                         listenOnce : function( type, opt_arg1, opt_arg2, opt_arg3 ){\r
155                                 X_EventDispatcher_once = true;\r
156                                 this.listen( type, opt_arg1, opt_arg2, opt_arg3 );\r
157                                 X_EventDispatcher_once = false;\r
158                                 return this;\r
159                         },\r
160                         \r
161                         off      : X_EventDispatcher_unlisten,\r
162                         unlisten : X_EventDispatcher_unlisten,\r
163 \r
164                 /**\r
165                  * イベントリスナの登録状況を真偽値で返す。戻り値が数値(index)の場合もあるが、これは内部のみで使用。\r
166                  * listening() と type を省略した場合、一つでも登録があれば true を返す。\r
167                  * listening( type ) と type だけを与えた場合、その type に登録があれば true を返す。\r
168                  * type と イベントリスナの組み合わせが登録されているかを調べる場合は、listen 時の thisObject や args(Array) も一致させて渡す必要がある。\r
169                  *  this.listen( [ 'myevent', 'yourevent' ], this, onMyEvent, args = [ 1, 'a' ] );\r
170                  *  this.listening( 'myevent', this, onMyEvent, args ) === true;\r
171                  * @this {X.EventDispatcher}\r
172                  * @return {(number|boolean)}\r
173                  * @param {(string|number)=} opt_type\r
174                  * @param {(listener|function|Array|callbackHash)=} opt_arg1\r
175                  * @param {(function|Array=} opt_arg2\r
176                  * @param {Array=} opt_arg3\r
177                  */                     \r
178                         listening : function( opt_type, opt_arg1, opt_arg2, opt_arg3 ){\r
179                                 var list = this._listeners,\r
180                                         unlistens, i, f, hash;\r
181                                 \r
182                                 if( opt_type === undefined ) return !!list;\r
183                                 if( !list || !( list = list[ opt_type ] ) ) return false;\r
184                                 if( opt_arg1 === undefined ) return true;\r
185                                 \r
186                                 if( opt_arg1.k ){\r
187                                         hash = opt_arg1;\r
188                                 } else {\r
189                                         hash = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
190                                 };\r
191                                 \r
192                                 if( ( unlistens = this._unlistens ) && ( unlistens = unlistens[ opt_type ] ) ){\r
193                                         for( i = unlistens.length; i; ){\r
194                                                 f = unlistens[ --i ];\r
195                                                 if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ) return false;\r
196                                         };\r
197                                 };\r
198                                 for( i = list.length; i; ){\r
199                                         f = list[ --i ];\r
200                                         if( f === hash || ( f.x === hash.x && f.f === hash.f && f.s === hash.s ) ){\r
201                                                 // index を要求された場合、lock されていない、または unlock なら index を返す\r
202                                                 return X_EventDispatcher_needsIndex ? ( X_EventDispatcher_unlock || !f.lock ? i : false ) : true;\r
203                                         };\r
204                                 };\r
205                                 return false;\r
206                         },\r
207 \r
208                 /**\r
209                  * delay(ミリ秒)後にイベントを dispatch する。戻り値は uid = X.Timer.add() のタイマーID(数値)。X.Timer.remove(uid) でタイマーを解除して dispatch を中止できる。\r
210                  * @this {X.EventDispatcher}\r
211                  * @param {(number|eventHash|string)=} delay ms 省略した場合は 0 として扱う asyncDispatch( 'myevent' ) -> asyncDispatch( 0, 'myevent' )\r
212                  * @param {(eventHash|string|number)=} e イベントを表す数値、文字列、{ type : XXX, ... } なオブジェクト\r
213                  * @return {number} X.Timer.add() の戻り値\r
214                  */                     \r
215                         asyncDispatch : function( delay, e ){\r
216                                 if( delay && e === undefined ){\r
217                                         e = delay;\r
218                                         delay = 0;\r
219                                 };\r
220                                 return X.Timer.add( delay, 1, this, this.dispatch, [ e ] );\r
221                         }\r
222                 }\r
223         );\r
224 \r
225 // ------------------------------------------------------------------------- //\r
226 // --- implements ---------------------------------------------------------- //\r
227 // ------------------------------------------------------------------------- //\r
228 \r
229 /**\r
230  * 登録されたイベントリスナを呼び出す。イベントリスナの返り値(数値)を OR したものを返す。イベントハッシュでなく、string|number を渡すと内部でイベントハッシュを作る\r
231  * @this {X.EventDispatcher}\r
232  * @return {number} X.Callback で定義された数値\r
233  * @param {(eventHash|string|number)} e\r
234  */\r
235 function X_EventDispatcher_dispatch( e ){\r
236         var list  = this[ '_listeners' ],\r
237                 ret   = X_Callback_NONE,\r
238                 type  = e[ 'type' ],\r
239                 unlistens, i, l, args, f, r, sysOnly;\r
240         \r
241         if( !list || !( list = list[ type || e ] ) ) return ret;\r
242         \r
243         // 数値, 文字が渡された場合\r
244         if( !type ){\r
245                 e = { type : type = e };\r
246         };\r
247         e.target        = e.target || this;\r
248         e.currentTarget = e.currentTarget || this;\r
249         \r
250         ++this._dispatching;\r
251         \r
252         // todo:\r
253         // type も保存\r
254         this._unlistens = this._unlistens || {};\r
255         unlistens = this._unlistens[ type ];\r
256         \r
257         for( i = 0; i < list.length; ++i ){\r
258                 f = list[ i ];\r
259                 if( !unlistens ){\r
260                         unlistens = this._unlistens[ type ];\r
261                 };\r
262                 if( unlistens && unlistens.indexOf( f ) !== -1 ) continue;\r
263                 \r
264                 //if( f !== X.emptyFunction ){\r
265                 //      if( f.k ){\r
266                                 r = X_Callback_proxyCallback( f, args || ( args = [ e ] ) );\r
267                 //      } else {\r
268                 //              r = f.call( this, e );\r
269                 //      };\r
270                 //};\r
271                 \r
272                 if( f.once || r & X_Callback_UN_LISTEN ){\r
273                         // dispatch 中に unlisten が作られることがある\r
274                         if( !unlistens ){\r
275                                 unlistens = this._unlistens || ( this._unlistens = {} );\r
276                                 unlistens = unlistens[ type ] || ( unlistens[ type ] = [] );\r
277                         };\r
278                         unlistens.indexOf( f ) === -1 && ( unlistens[ unlistens.length ] = f );\r
279                 };\r
280 \r
281                 if( r & X.Callback.STOP_NOW ){\r
282                         sysOnly = true;\r
283                 };\r
284                 ret |= X.Type.isFinite( r ) ? r : 0;\r
285         };\r
286         \r
287         if( ( --this._dispatching ) === 0 ){\r
288                 // dispatch 中に unlisten された要素の削除\r
289                 unlistens = this._unlistens;\r
290                 delete this._dispatching;\r
291                 delete this._unlistens;                                 \r
292                 // _unlistens に入っている callbackHash は、lock をクリアしている\r
293                 X_EventDispatcher_unlock = true;\r
294                 for( type in unlistens ){\r
295                         list = unlistens[ type ];\r
296                         for( i = list.length; i; ){\r
297                                 this.unlisten( type, list[ --i ] );\r
298                         };\r
299                         list.length = 0;\r
300                         delete unlistens[ type ];\r
301                 };\r
302                 X_EventDispatcher_unlock = false;\r
303                 \r
304                 if( this._killReserved ){\r
305                         this.kill();\r
306                 } else\r
307                 if( list = this._reserves ){\r
308                         for( i = 0, l = list.length; i < l; ++i ){\r
309                                 f = list[ i ];\r
310                                 X_EventDispatcher_once = f[ 4 ];\r
311                                 X_EventDispatcher_lock = f[ 5 ];\r
312                                 this.listen( f[ 0 ], f[ 1 ], f[ 2 ], f[ 3 ] );\r
313                                 X_EventDispatcher_once = false;\r
314                                 X_EventDispatcher_lock = false;\r
315                                 f.length = 0;\r
316                         };\r
317                         list.length = 0;\r
318                         delete this._reserves;\r
319                 };\r
320         };\r
321         \r
322         return ret;\r
323 };\r
324 \r
325 /**\r
326  * \r
327  * @this {X.EventDispatcher}\r
328  * @return {X.EventDispatcher}\r
329  * @param {(string|number|Array.<(string,number)>)} type\r
330  * @param {(listener|function|Array)=} opt_arg1\r
331  * @param {(function|Array=} opt_arg2\r
332  * @param {Array=} opt_arg3\r
333  */\r
334 function X_EventDispatcher_listen( type, opt_arg1, opt_arg2, opt_arg3 ){\r
335         var list = this._listeners,\r
336                 i, raw, add, f;\r
337 \r
338         if( !type ) return this;\r
339         \r
340         if( this._dispatching ){\r
341                 if( !this._reserves ) this._reserves = [];\r
342                 this._reserves[ this._reserves.length ] = [ type, opt_arg1, opt_arg2, opt_arg3, X_EventDispatcher_once, X_EventDispatcher_lock ];\r
343                 return this;\r
344         };\r
345         \r
346         if( X.Type.isArray( type ) ){\r
347                 for( i = type.length; i; ){\r
348                         this.listen( type[ --i ], opt_arg1, opt_arg2, opt_arg3 );\r
349                 };\r
350                 return this;\r
351         };\r
352         \r
353         raw = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this );\r
354         add = raw && ( !list || !list[ type ] ) && X.Type.isString( type );\r
355         \r
356         if( this.listening( type, opt_arg1 || this, opt_arg2, opt_arg3 ) ) return this;\r
357 \r
358         if( !list ) list = this._listeners = {};\r
359         if( !( list = list[ type ] ) ) list = this._listeners[ type ] = [];\r
360         \r
361         add && X_EventDispatcher_addEvent( this, type, raw, list );\r
362         \r
363         f = X_Callback_classifyCallbackArgs( opt_arg1, opt_arg2, opt_arg3, this );\r
364         list[ list.length ] = f;\r
365         f.once = X_EventDispatcher_once;\r
366         f.lock = X_EventDispatcher_lock;\r
367         \r
368         return this;\r
369 };\r
370 \r
371 /*\r
372  * X_EventDispatcher_systemUnlisten 経由でないと解除できないリスナの登録\r
373  */\r
374 function X_EventDispatcher_systemListen( that, type, opt_arg1, opt_arg2, opt_arg3 ){\r
375         X_EventDispatcher_lock = true;\r
376         that.listen( type, opt_arg1, opt_arg2, opt_arg3 );\r
377         X_EventDispatcher_lock = false;\r
378 };\r
379 \r
380 // TODO this.listen(type) は this リスナの登録なのに、this.unlisten(type)は全てのtypeの削除、と不一致\r
381 \r
382 /**\r
383  * \r
384  * @this {X.EventDispatcher}\r
385  * @return {X.EventDispatcher}\r
386  * @param {(string|number|Array.<(string,number)>)=} opt_type\r
387  * @param {(listener|function|Array)=} opt_arg1\r
388  * @param {(function|Array=} opt_arg2\r
389  * @param {Array=} opt_arg3\r
390  */\r
391 function X_EventDispatcher_unlisten( opt_type, opt_arg1, opt_arg2, opt_arg3 ){\r
392         var list = this._listeners,\r
393                 _list, reserves, unlistens, i, f, raw, empty;\r
394         if( !list ) return this;\r
395         \r
396         if( X.Type.isArray( opt_type ) ){\r
397                 for( i = opt_type.length; i; ){\r
398                         this.unlisten( opt_type[ --i ], opt_arg1, opt_arg2, opt_arg3 );\r
399                         if( !opt_type[ i ] ){\r
400                                 alert( '不正な unlisten Array' );\r
401                         };\r
402                 };\r
403                 return this;\r
404         };\r
405         \r
406         if( opt_type === undefined ){\r
407                 // 全て削除\r
408                 for( opt_type in list ){\r
409                         _list = list[ opt_type ];\r
410                         for( i = _list.length; i; ){\r
411                                 this.unlisten( opt_type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
412                         };\r
413                         // this.unlisten( opt_type ); これは無茶!\r
414                 };\r
415                 return this;\r
416         } else\r
417         if( opt_arg1 === undefined ){\r
418                 // 同一タイプを全て削除\r
419                 if( _list = list[ opt_type ] ){\r
420                         for( i = _list.length; i; ){\r
421                                 this.unlisten( opt_type, _list[ --i ] ); // override されていることがあるので、必ず unlisten を使用\r
422                         };\r
423                 };\r
424                 return this;\r
425         } else\r
426         if( reserves = this._reserves ){\r
427                 for( i = reserves.length; i; ){\r
428                         f = reserves[ --i ];\r
429                         if( f[ 0 ] === opt_type && f[ 1 ] === opt_arg1 && f[ 2 ] === opt_arg2 && f[ 3 ] === opt_arg3 ){\r
430                                 reserves.splice( i, 1 );\r
431                                 if( !reserves.legth ) delete this._reserves;\r
432                                 return this;\r
433                         };\r
434                 };\r
435         };\r
436         \r
437         X_EventDispatcher_needsIndex = true;\r
438         i = this.listening( opt_type, opt_arg1, opt_arg2, opt_arg3 );\r
439         X_EventDispatcher_needsIndex = false;\r
440         if( i === false ) return this;\r
441 \r
442         f = ( _list = list[ opt_type ] )[ i ];\r
443         // _unlistens に入っている callbackHash は、lock をクリアしている\r
444         if( unlistens = this._unlistens ){\r
445                 ( unlistens = unlistens[ opt_type ] ) ?\r
446                         ( unlistens[ unlistens.length ] = f ) :\r
447                         ( this._unlistens[ opt_type ] = [ f ] );\r
448         } else {\r
449                 delete f.once;\r
450                 _list.splice( i, 1 );\r
451                 if( !_list.length ){\r
452                         raw  = this._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( this );\r
453                         delete list[ opt_type ];\r
454                         if( empty = X_Object_isEmpty( list ) ) delete this._listeners;\r
455                         if( raw && '' + parseFloat( opt_type ) !== '' + opt_type ){ // 数字イベントの除外\r
456                                 X_EventDispatcher_removeEvent( this, opt_type, raw, _list, !empty );\r
457                         };\r
458                 };\r
459         };\r
460         return this;\r
461 };\r
462 \r
463 /*\r
464  * X_EventDispatcher_systemListen から登録したイベントの解除\r
465  */\r
466 function X_EventDispatcher_systemUnlisten( that, type, opt_arg1, opt_arg2, opt_arg3 ){\r
467         X_EventDispatcher_unlock = true;\r
468         that.unlisten( type, opt_arg1, opt_arg2, opt_arg3 );\r
469         X_EventDispatcher_unlock = false;\r
470 };\r
471 \r
472 function X_EventDispatcher_addEvent( that, type, raw, list ){\r
473         var i;\r
474         X_EventDispatcher_lock || ( type = X_Event_Rename[ type ] || type );\r
475         \r
476         if( X.Type.isArray( type ) ){\r
477                 for( i = type.length; i; ){\r
478                         X_EventDispatcher_systemListen( that, type[ --i ], X.emptyFunction );\r
479                         console.log( 'X_EventDispatcher_systemListen ' + type[ i ] );\r
480                 };\r
481         } else {\r
482                 X_EventDispatcher_actualAddEvent( that, type, raw, list );\r
483         };\r
484 };\r
485 \r
486 var X_EventDispatcher_actualAddEvent =\r
487         // Days on the Moon DOM Events とブラウザの実装 \r
488         // http://nanto.asablo.jp/blog/2007/03/23/1339502\r
489         // Safari 2 では関数オブジェクトしか EventListener として使えませんが、Safari のナイトリービルドでは handleEvent メソッドを持つオブジェクトも EventListener として使えるようです。\r
490         X_UA_EVENT.W3C /* && ( X_UA.WebKit < 525.13 || X_UA.Opera7 || X_UA.NetFront < 4 ) */ ? // Safari3-\r
491                 (function( that, type, raw, list ){\r
492                         if( that._isXHR && X_UA.Opera < 12 ){\r
493                                 // Opera11- の XHR は event オブジェクトが返らないため, eventType 毎に callback を指定する addEventListener もない\r
494                                 raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] );\r
495                         } else\r
496                         if( that._isSilverlight ){\r
497                                 list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
498                                 list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
499                         } else {                \r
500                                 that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
501 \r
502                                 if( raw.addEventListener ){\r
503                                         raw.addEventListener( type, that._handleEvent, false );\r
504                                 } else {\r
505                                         // Safari は Image, Opera7 は window\r
506                                         raw[ 'on' + type ] = that._handleEvent;\r
507                                 };\r
508                         };\r
509                 }) :\r
510         X_UA_EVENT.IE ?\r
511                 (function( that, type, raw, list ){\r
512                         if( that._isXHR ){\r
513                                 // ie8- の XHR は window.event が更新されないため, eventType 毎に callback を指定する\r
514                                 raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] );\r
515                         } else                                  \r
516                         if( that._isSilverlight ){\r
517                                 list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
518                                 list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
519                         } else {                                \r
520                                 that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
521                                 \r
522                                 if( raw.attachEvent ){\r
523                                         raw.attachEvent( 'on' + type, that._handleEvent );\r
524                                 } else {\r
525                                         raw[ 'on' + type ] = that._handleEvent;\r
526                                 };\r
527                         };\r
528                 }) :\r
529                 (function( that, type, raw, list ){\r
530                         if( that._isXHR ){\r
531                                 // ie4 mobile は XHR をサポート!\r
532                                 raw[ 'on' + type ] = X_Callback_create( that, X_EventDispatcher_dispatch, [ type ] );\r
533                         } else\r
534                         if( that._isSilverlight ){\r
535                                 // DOM0 で Silverlight ってあるの?\r
536                                 list.slcallback = X_Callback_create( that, X_EventDispatcher_sliverLightDispatch, [ type ] );\r
537                                 list.sltoken    = raw.AddEventListener( type, list.slcallback );\r
538                         } else {\r
539                                 raw[ 'on' + type ] = that._handleEvent || ( that._handleEvent = X_Callback_create( that, X_EventDispatcher_actualHandleEvent ) );\r
540                         };\r
541                 });\r
542 \r
543 /*\r
544  * Silverlight のイベントの概要\r
545  * http://msdn.microsoft.com/ja-jp/library/cc189018%28v=vs.95%29.aspx#the_sender_parameter_and_event_data\r
546  */\r
547 function X_EventDispatcher_sliverLightDispatch( sender, e, type ){\r
548         return this.dispatch( type );\r
549 };\r
550 \r
551 function X_EventDispatcher_removeEvent( that, type, raw, list, skip ){\r
552         var i;\r
553         X_EventDispatcher_unlock || ( type = X_Event_Rename[ type ] || type );\r
554         \r
555         if( X.Type.isArray( type ) ){\r
556                 for( i = type.length; i; ){\r
557                         X_EventDispatcher_systemUnlisten( that, type[ --i ], X.emptyFunction );\r
558                 };\r
559         } else {\r
560                 X_EventDispatcher_actualRemoveEvent( that, type, raw, list, skip );\r
561         };\r
562 };\r
563 \r
564 var X_EventDispatcher_actualRemoveEvent =\r
565         X_UA_EVENT.W3C /*&& ( X_UA.WebKit < 525.13 || X_UA.Opera7 || X_UA.NetFront < 4 )*/ ? // Safari3-\r
566                 (function( that, type, raw, list, skip ){\r
567                         if( that._isXHR && X_UA.Opera < 12 ){\r
568                                 X_Callback_correct( raw[ 'on' + type ] );\r
569                                 raw[ 'on' + type ] = '';\r
570                         } else  \r
571                         if( that._isSilverlight ){\r
572                                 raw.RemoveEventListener( type, list.sltoken ); // token\r
573                                 X_Callback_correct( list.slcallback );\r
574                                 delete list.sltoken;\r
575                                 delete list.slcallback;\r
576                         } else {\r
577                                 if( raw.addEventListener ){\r
578                                         raw.removeEventListener( type, that._handleEvent, false );\r
579                                 } else {\r
580                                         raw[ 'on' + type ] = null;\r
581                                 };\r
582                                 if( !skip ){\r
583                                         X_Callback_correct( that._handleEvent );\r
584                                         delete that._handleEvent;\r
585                                 };\r
586                         };\r
587                 }) :\r
588         X_UA_EVENT.IE ?\r
589                 (function( that, type, raw, list, skip ){\r
590                         if( that._isXHR ){\r
591                                 X_Callback_correct( raw[ 'on' + type ] );\r
592                                 raw[ 'on' + type ] = X.emptyFunction;\r
593                                 raw[ 'on' + type ] = '';\r
594                         } else  \r
595                         if( that._isSilverlight ){\r
596                                 raw.RemoveEventListener( type, list.sltoken ); // token\r
597                                 X_Callback_correct( list.slcallback );\r
598                                 delete list.sltoken;\r
599                                 delete list.slcallback;\r
600                         } else {\r
601                                 if( raw.attachEvent ){\r
602                                         raw.detachEvent( 'on' + type, that._handleEvent );\r
603                                 } else {\r
604                                         raw[ 'on' + type ] = X.emptyFunction;\r
605                                         raw[ 'on' + type ] = '';\r
606                                 };\r
607                                 \r
608                                 if( !skip ){\r
609                                         X_Callback_correct( that._handleEvent );\r
610                                         delete that._handleEvent;\r
611                                 };\r
612                         };\r
613                 }) :\r
614                 (function( that, type, raw, list, skip ){\r
615                         if( that._isXHR ){\r
616                                 X_Callback_correct( raw[ 'on' + type ] );\r
617                                 raw[ 'on' + type ] = X.emptyFunction;\r
618                                 raw[ 'on' + type ] = '';\r
619                         } else\r
620                         if( that._isSilverlight ){\r
621                                 raw.RemoveEventListener( type, list.sltoken ); // token\r
622                                 X_Callback_correct( list.slcallback );\r
623                                 delete list.sltoken;\r
624                                 delete list.slcallback;\r
625                         } else {\r
626                                 raw[ 'on' + type ] = X.emptyFunction;\r
627                                 raw[ 'on' + type ] = '';\r
628                                 \r
629                                 if( !skip ){\r
630                                         X_Callback_correct( that._handleEvent );\r
631                                         delete that._handleEvent;\r
632                                 };\r
633                         };\r
634                 });\r
635 \r
636 \r
637 // handleEvent を拡張可能にするために、クロージャに移動した\r
638 // Is this in regard to the Safari 1.x preventDefault bug on click/dblclick?\r
639 // https://groups.google.com/forum/#!msg/comp.lang.javascript/uYEuCHjHxnw/yKoHtZJPa1QJ\r
640 var X_EventDispatcher_actualHandleEvent =\r
641         X_UA_EVENT.IE4 || X_UA_EVENT.IE ? // ie45678 EVENT_IE & EVENT_DOM0 for ie4\r
642                 (function(){\r
643                         var ret;\r
644                         \r
645                         //if( event.type === 'readystatechange' && this._tag && X.Dom.Event._LOAD_FIX_TAGS[ this._tag ] ){\r
646                                 //type = 'readystatechange';\r
647                         //};\r
648                         \r
649                         ret = this.dispatch( new X.Dom.Event( event, this, this._rawObject ) );\r
650 \r
651                         if( ret & X.Callback.STOP_PROPAGATION ){\r
652                                 event.cancelBubble = true;\r
653                         };\r
654                         if( ret & X.Callback.PREVENT_DEFAULT ){\r
655                                 this._tag === 'A' && this._rawObject.blur();\r
656                                 return event.returnValue = false;\r
657                         };\r
658                 }) :\r
659         //X_UA_EVENT.W3C & X_UA_EVENT.DOM0\r
660                 (function( e ){\r
661                         var ev  = new X.Dom.Event( e, this ),\r
662                                 ret = X_Callback_NONE,\r
663                                 i, l;\r
664                         console.log( '>>>>>>>>>> ' + e.type );\r
665                         // touch event -> pointer\r
666                         if( X.Type.isArray( ev ) ){\r
667                                 if( ev.length === 0 ){\r
668                                         // TouchEvent の後に発生した MouseEvent のキャンセル\r
669                                         ret = X.Callback.STOP_PROPAGATION | X.Callback.PREVENT_DEFAULT;\r
670                                 } else {\r
671                                         for( i = 0, l = ev.length; i < l; ++i ){\r
672                                                 console.log( 'handleEvent ' + ev[ i ].type );\r
673                                                 ret |= this.dispatch( ev[ i ] ) || 0;\r
674                                         };                              \r
675                                 };\r
676                         } else {\r
677                                 ret = this.dispatch( ev );\r
678                         };\r
679                         \r
680                         if( ret & X.Callback.STOP_PROPAGATION ){\r
681                                 e.stopPropagation();\r
682                         };\r
683                         if( ret & X.Callback.PREVENT_DEFAULT ){\r
684                                 this._tag === 'A' && this._rawObject.blur();\r
685                                 e.preventDefault();\r
686                                 if( X_UA.WebKit < 525.13 ){ // Safari3-\r
687                                         if( e.type === 'click' || e.type === 'dbclick' ){\r
688                                                 X_EventDispatcher_safariPreventDefault = true;\r
689                                         };\r
690                                 };\r
691                                 return false;\r
692                         };\r
693                 });\r
694 \r
695 if( X_UA.WebKit < 525.13 ){ // Safari3-\r
696         document.documentElement.onclick =\r
697         document.documentElement.ondbclick = function( e ){\r
698                         if( X_EventDispatcher_safariPreventDefault ){\r
699                                 X_EventDispatcher_safariPreventDefault = false;\r
700                                 e.preventDefault();\r
701                                 return false;\r
702                         };\r
703                 };\r
704 };\r
705 \r
706 // イベントの退避、dom が画面から抜かれる場合に実施しておく\r
707 // 退避したイベントの復帰\r
708 function X_EventDispatcher_toggleAllEvents( that, add ){\r
709         var list = that._listeners,\r
710                 raw  = that._rawObject || X_UA_DOM.IE4 && X_Node__ie4getRawNode( that ),\r
711                 f    = add ? X_EventDispatcher_addEvent : X_EventDispatcher_removeEvent,\r
712                 type;\r
713         if( !list || !raw ) return;\r
714         for( type in list ){\r
715                 // 数字イベントの除外\r
716                 if( '' + parseFloat( type ) !== type ){\r
717                         // TODO type rename はここ\r
718                         f( that, type, raw, list[ type ], true );\r
719                 };\r
720         };\r
721 };\r
722 \r
723 \r
724 console.log( 'X.Core.EventDispatcher' );\r