OSDN Git Service

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