OSDN Git Service

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