OSDN Git Service

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