/** * このクラスでは、「イベントディスクリプタ」の配列の再生を管理します。「イベントディスクリプタ」のコンテンツの詳細については、{@link Ext.ux.event.Recorder}を参照してください。{@link Ext.ux.event.Recorder}クラスによって記録されたイベントは、このクラスへの入力として使用できるようにデザインされます。 * * このクラスの最もシンプルな使用方法は、{@link #eventQueue}を使用してインスタンス化し、{@link #method-start}を呼び出すことです。以下のようにします。 * * var player = Ext.create('Ext.ux.event.Player', { * eventQueue: [ ... ], * speed: 2, // play at 2x speed * listeners: { * stop: function () { * player = null; // all done * } * } * }); * * player.start(); * * より複雑な使用方法として、特定イベントの再生後にキーフレーム生成を実装することが挙げられます。 * * var player = Ext.create('Ext.ux.event.Player', { * eventQueue: [ ... ], * keyFrameEvents: { * click: true * }, * listeners: { * stop: function () { * // play has completed... probably time for another keyframe... * player = null; * }, * keyframe: onKeyFrame * } * }); * * player.start(); * * キーフレームを即座に(同期的に)操作できる場合、リスナは次のようになります。 * * function onKeyFrame () { * handleKeyFrame(); * } * * キーフレームイベントが常に非同期的に操作される場合、イベントリスナは少しだけ複雑になり、次のようになります。 * * function onKeyFrame (p, eventDescriptor) { * eventDescriptor.defer(); // pause event playback... * * handleKeyFrame(function () { * eventDescriptor.finish(); // ...resume event playback * }); * } * * 最後に、キーフレームイベントが同期的に操作されることも非同期的に操作されることもある場合(ブラウザによって操作が異なるなど)、やや複雑なリスナが必要となります。 * * function onKeyFrame (p, eventDescriptor) { * var async; * * handleKeyFrame(function () { * // either this callback is being called immediately by handleKeyFrame (in * // which case async is undefined) or it is being called later (in which case * // async will be true). * * if (async) { * eventDescriptor.finish(); * } else { * async = false; * } * }); * * // either the callback was called (and async is now false) or it was not * // called (and async remains undefined). * * if (async !== false) { * eventDescriptor.defer(); * async = true; // let the callback know that we have gone async * } * } */ Ext.define('Ext.ux.event.Player', function (Player) { var defaults = {}, mouseEvents = {}, keyEvents = {}, doc, //HTML events supported uiEvents = {}, //events that bubble by default bubbleEvents = { //scroll: 1, resize: 1, reset: 1, submit: 1, change: 1, select: 1, error: 1, abort: 1 }; Ext.each([ 'click', 'dblclick', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'mousemove' ], function (type) { bubbleEvents[type] = defaults[type] = mouseEvents[type] = { bubbles: true, cancelable: (type != "mousemove"), // mousemove cannot be cancelled detail: 1, screenX: 0, screenY: 0, clientX: 0, clientY: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, button: 0 }; }); Ext.each([ 'keydown', 'keyup', 'keypress' ], function (type) { bubbleEvents[type] = defaults[type] = keyEvents[type] = { bubbles: true, cancelable: true, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, keyCode: 0, charCode: 0 }; }); Ext.each([ 'blur', 'change', 'focus', 'resize', 'scroll', 'select' ], function (type) { defaults[type] = uiEvents[type] = { bubbles: (type in bubbleEvents), cancelable: false, detail: 1 }; }); var inputSpecialKeys = { 8: function (target, start, end) { // backspace: 8, if (start < end) { target.value = target.value.substring(0, start) + target.value.substring(end); } else if (start > 0) { target.value = target.value.substring(0, --start) + target.value.substring(end); } this.setTextSelection(target, start, start); }, 46: function (target, start, end) { // delete: 46 if (start < end) { target.value = target.value.substring(0, start) + target.value.substring(end); } else if (start < target.value.length - 1) { target.value = target.value.substring(0, start) + target.value.substring(start+1); } this.setTextSelection(target, start, start); } }; return { extend: 'Ext.ux.event.Driver', /** * @cfg {Array} eventQueue 再生するためのイベントの待ち行列。これは{@link #method-start}メソッドが呼び出される前に提供されなければなりません。 */ /** * @cfg {Object} keyFrameEvents keyframeイベントを生成するイベントを記述するオブジェクト。例えば `{ click: true }`を設定すると、`click`イベントの後にkeyframeイベントを生成します。 */ keyFrameEvents: { click: true }, /** * @cfg {Boolean} pauseForAnimations trueにすると、アニメーション中はイベントの再生を休止し、falseにすると、アニメーションを無視してイベントを再生します。デフォルトはtrueです。 */ pauseForAnimations: true, /** * @cfg {Number} speed 再生速度の乗算器です。デフォルトは1.0で、記録された速度で再生されます。値が2の場合は2倍の速度で再生されます。 */ speed: 1.0, stallTime: 0, _inputSpecialKeys: { INPUT: inputSpecialKeys, TEXTAREA: Ext.apply({ //13: function (target, start, end) { // enter: 8, //TODO ? //} }, inputSpecialKeys) }, tagPathRegEx: /(\w+)(?:\[(\d+)\])?/, /** * @event beforeplay * イベントが再生される前に発火します。 * @param {Ext.ux.event.Player} this * @param {Object} eventDescriptor 再生されようとしているイベントのディスクリプタ。 */ /** * @event keyframe * このプレイヤーがキーフレームに達したときに発火します。一般的に、これは`click`などのイベントが挿入された結果として起こるアニメーションが完了した後のことです。 * @param {Ext.ux.event.Player} this * @param {Object} eventDescriptor keyframeイベントのディスクリプタ。 */ constructor: function (config) { var me = this; me.callParent(arguments); me.timerFn = function () { me.onTick(); }; me.attachTo = me.attachTo || window; doc = me.attachTo.document; }, /** * 要素に設定されたXPathのような記述を返します。 * @param {String} xpath 要素のXPathのような記述。 * @return {HTMLElement} */ getElementFromXPath: function (xpath) { var me = this, parts = xpath.split('/'), regex = me.tagPathRegEx, i, n, m, count, tag, child, el = me.attachTo.document; el = (parts[0] == '~') ? el.body : el.getElementById(parts[0].substring(1)); // remove '#' for (i = 1, n = parts.length; el && i < n; ++i) { m = regex.exec(parts[i]); count = m[2] ? parseInt(m[2], 10) : 1; tag = m[1].toUpperCase(); for (child = el.firstChild; child; child = child.nextSibling) { if (child.tagName == tag) { if (count == 1) { break; } --count; } } el = child; } return el; }, // Moving across a line break only counts as moving one character in a TextRange, whereas a line break in // the textarea value is two characters. This function corrects for that by converting a text offset into a // range character offset by subtracting one character for every line break in the textarea prior to the // offset offsetToRangeCharacterMove: function(el, offset) { return offset - (el.value.slice(0, offset).split("\r\n").length - 1); }, setTextSelection: function (el, startOffset, endOffset) { // See https://code.google.com/p/rangyinputs/source/browse/trunk/rangyinputs_jquery.js if (startOffset < 0) { startOffset += el.value.length; } if (endOffset == null) { endOffset = startOffset; } if (endOffset < 0) { endOffset += el.value.length; } if (typeof el.selectionStart === "number") { el.selectionStart = startOffset; el.selectionEnd = endOffset; } else { var range = el.createTextRange(); var startCharMove = this.offsetToRangeCharacterMove(el, startOffset); range.collapse(true); if (startOffset == endOffset) { range.move("character", startCharMove); } else { range.moveEnd("character", this.offsetToRangeCharacterMove(el, endOffset)); range.moveStart("character", startCharMove); } range.select(); } }, getTimeIndex: function () { var t = this.getTimestamp() - this.stallTime; return t * this.speed; }, makeToken: function (eventDescriptor, signal) { var me = this, t0; eventDescriptor[signal] = true; eventDescriptor.defer = function () { eventDescriptor[signal] = false; t0 = me.getTime(); }; eventDescriptor.finish = function () { eventDescriptor[signal] = true; me.stallTime += me.getTime() - t0; me.schedule(); }; }, /** * このメソッドは、次のイベントを準備するために、イベントが再生された後に呼び出されます。 * @param {Object} eventDescriptor ちょうど再生されているイベントのディスクリプタ。 */ nextEvent: function (eventDescriptor) { var me = this, index = ++me.queueIndex; // keyframe events are inserted after a keyFrameEvent is played. if (me.keyFrameEvents[eventDescriptor.type]) { Ext.Array.insert(me.eventQueue, index, [ {keyframe: true, ts: eventDescriptor.ts} ]); } }, /** * このメソッドは、キューの先頭でイベントのディスクリプタを返します。これはイベントを待ち行列から外すわけではありません。反復呼び出しにより同じオブジェクトが返されます({@link #nextEvent}が呼び出されるまで)。 */ peekEvent: function () { return this.eventQueue[this.queueIndex] || null; }, /** * キューのイベントをイベントの配列で置き換えます。これは、マルチステップの擬似要素をロールアップして再生時に展開するのによく使用されます。派生クラスでこれを行う手順は次のとおりです。 * * Ext.define('My.Player', { * extend: 'Ext.ux.event.Player', * * peekEvent: function () { * var event = this.callParent(); * * if (event.multiStepSpecial) { * this.replaceEvent(null, [ * ... expand to actual events * ]); * * event = this.callParent(); // get the new next event * } * * return event; * } * }); * * このメソッドを使用すると、置換するイベントの`beforeplay`フック(ある場合)が最初の新規イベントに置かれ、`afterplay`フック(ある場合)が最後の新規イベントに置かれます。 * * @param {Number} index 置換するキューのインデックス。現在の`queueIndex`にあるイベントを置換するには`null`を渡します。 * @param {Event[]} events 指定されたイベントを置換するイベントの配列。 */ replaceEvent: function (index, events) { for (var t, i = 0, n = events.length; i < n; ++i) { if (i) { t = events[i-1]; delete t.afterplay; delete t.screenshot; delete events[i].beforeplay; } } Ext.Array.replace(this.eventQueue, (index == null) ? this.queueIndex : index, 1, events); }, /** * このメソッドはタイムインデックスに達するまでイベントを待ち行列から外し、挿入します。イベントの準備が整っていない場合(タイムインデックスに基づく)、このメソッドは何もしません。 * @return {Boolean} これ以上することがなければtrue、今現在することがあればfalse。 */ processEvents: function () { var me = this, animations = me.pauseForAnimations && me.attachTo.Ext.fx.Manager.items, eventDescriptor; while ((eventDescriptor = me.peekEvent()) !== null) { if (animations && animations.getCount()) { return true; } if (eventDescriptor.keyframe) { if (!me.processKeyFrame(eventDescriptor)) { return false; } me.nextEvent(eventDescriptor); } else if (eventDescriptor.ts <= me.getTimeIndex() && me.fireEvent('beforeplay', me, eventDescriptor) !== false && me.playEvent(eventDescriptor)) { me.nextEvent(eventDescriptor); } else { return true; } } me.stop(); return false; }, /** * このメソッドはキーフレームに達したときに呼び出されます。これはkeyframeイベントを発火させます。keyframeイベントが処理されると、trueが返されます。そうでなければ、falseが返されます。 * @param {Object} eventDescriptor keyframeのイベントディスクリプタ。 * @return {Boolean} keyframeイベントが処理されるとtrue、そうでなければfalse。 */ processKeyFrame: function (eventDescriptor) { var me = this; // only fire keyframe event (and setup the eventDescriptor) once... if (!eventDescriptor.defer) { me.makeToken(eventDescriptor, 'done'); me.fireEvent('keyframe', me, eventDescriptor); } return eventDescriptor.done; }, /** * 指定されたターゲットに与えられたイベントを導入するために呼び出します。 * @param {HTMLElement} target イベントのターゲット。 * @param {Object} event 導入するイベント。このオブジェクトのプロパティは標準のDOMイベントですが、`type`プロパティによって異なります。イベントのタイプとそのプロパティに関する詳細は、クラスのドキュメンテーションを参照してください。 */ injectEvent: function (target, event) { var me = this, type = event.type, options = Ext.apply({}, event, defaults[type]), handler; if (type === 'type') { handler = me._inputSpecialKeys[target.tagName]; if (handler) { return me.injectTypeInputEvent(target, event, handler); } return me.injectTypeEvent(target, event); } if (type === 'focus' && target.focus) { target.focus(); return true; } if (type === 'blur' && target.blur) { target.blur(); return true; } if (type === 'scroll') { target.scrollLeft = event.pos[0]; target.scrollTop = event.pos[1]; return true; } if (type === 'mduclick') { return me.injectEvent(target, Ext.applyIf({ type: 'mousedown' }, event)) && me.injectEvent(target, Ext.applyIf({ type: 'mouseup' }, event)) && me.injectEvent(target, Ext.applyIf({ type: 'click' }, event)); } if (mouseEvents[type]){ return Player.injectMouseEvent(target, options, me.attachTo); } if (keyEvents[type]){ return Player.injectKeyEvent(target, options, me.attachTo); } if (uiEvents[type]){ return Player.injectUIEvent(target, type, options.bubbles, options.cancelable, options.view || me.attachTo, options.detail); } return false; }, injectTypeEvent: function (target, event) { var me = this, text = event.text, xlat = [], ch, chUp, i, n, sel, upper, isInput; if (text) { delete event.text; upper = text.toUpperCase(); for (i = 0, n = text.length; i < n; ++i) { ch = text.charCodeAt(i); chUp = upper.charCodeAt(i); xlat.push( Ext.applyIf({type: 'keydown', charCode: chUp, keyCode: chUp}, event), Ext.applyIf({type: 'keypress', charCode: ch, keyCode: ch}, event), Ext.applyIf({type: 'keyup', charCode: chUp, keyCode: chUp}, event) ); } } else { xlat.push( Ext.applyIf({type: 'keydown', charCode: event.keyCode}, event), Ext.applyIf({type: 'keyup', charCode: event.keyCode}, event) ); } for (i = 0, n = xlat.length; i < n; ++i) { me.injectEvent(target, xlat[i]); } return true; }, injectTypeInputEvent: function (target, event, handler) { var me = this, text = event.text, sel, n; if (handler) { sel = me.getTextSelection(target); if (text) { n = sel[0]; target.value = target.value.substring(0, n) + text + target.value.substring(sel[1]); n += text.length; me.setTextSelection(target, n, n); } else { if (!(handler = handler[event.keyCode])) { // no handler for the special key for this element if ('caret' in event) { me.setTextSelection(target, event.caret, event.caret); } else if (event.selection) { me.setTextSelection(target, event.selection[0], event.selection[1]); } return me.injectTypeEvent(target, event); } handler.call(this, target, sel[0], sel[1]); return true; } } return true; }, playEvent: function (eventDescriptor) { var me = this, target = me.getElementFromXPath(eventDescriptor.target), event; if (!target) { // not present (yet)... wait for element present... // TODO - need a timeout here return false; } if (!me.playEventHook(eventDescriptor, 'beforeplay')) { return false; } if (!eventDescriptor.injected) { eventDescriptor.injected = true; event = me.translateEvent(eventDescriptor, target); me.injectEvent(target, event); } return me.playEventHook(eventDescriptor, 'afterplay'); }, playEventHook: function (eventDescriptor, hookName) { var me = this, doneName = hookName + '.done', firedName = hookName + '.fired', hook = eventDescriptor[hookName]; if (hook && !eventDescriptor[doneName]) { if (!eventDescriptor[firedName]) { eventDescriptor[firedName] = true; me.makeToken(eventDescriptor, doneName); if (me.eventScope && Ext.isString(hook)) { hook = me.eventScope[hook]; } if (hook) { hook.call(me.eventScope || me, eventDescriptor); } } return false; } return true; }, schedule: function () { var me = this; if (!me.timer) { me.timer = setTimeout(me.timerFn, 10); } }, _translateAcross: [ 'type', 'button', 'charCode', 'keyCode', 'caret', 'pos', 'text', 'selection' ], translateEvent: function (eventDescriptor, target) { var me = this, event = {}, modKeys = eventDescriptor.modKeys || '', names = me._translateAcross, i = names.length, name, xy; while (i--) { name = names[i]; if (name in eventDescriptor) { event[name] = eventDescriptor[name]; } } event.altKey = modKeys.indexOf('A') > 0; event.ctrlKey = modKeys.indexOf('C') > 0; event.metaKey = modKeys.indexOf('M') > 0; event.shiftKey = modKeys.indexOf('S') > 0; if (target && 'x' in eventDescriptor) { xy = Ext.fly(target).getXY(); xy[0] += eventDescriptor.x; xy[1] += eventDescriptor.y; } else if ('x' in eventDescriptor) { xy = [ eventDescriptor.x, eventDescriptor.y ]; } else if ('px' in eventDescriptor) { xy = [ eventDescriptor.px, eventDescriptor.py ]; } if (xy) { event.clientX = event.screenX = xy[0]; event.clientY = event.screenY = xy[1]; } if (eventDescriptor.key) { event.keyCode = me.specialKeysByName[eventDescriptor.key]; } if (eventDescriptor.type === 'wheel') { if ('onwheel' in me.attachTo.document) { event.wheelX = eventDescriptor.dx; event.wheelY = eventDescriptor.dy; } else { event.type = 'mousewheel'; event.wheelDeltaX = -40 * eventDescriptor.dx; event.wheelDeltaY = event.wheelDelta = -40 * eventDescriptor.dy; } } return event; }, //--------------------------------- // Driver overrides onStart: function () { var me = this; me.queueIndex = 0; me.schedule(); }, onStop: function () { var me = this; if (me.timer) { clearTimeout(me.timer); me.timer = null; } }, //--------------------------------- onTick: function () { var me = this; me.timer = null; if (me.processEvents()) { me.schedule(); } }, statics: { ieButtonCodeMap: { 0: 1, 1: 4, 2: 2 }, /* * 指定されたイベント情報を使用してキーイベントを挿入し、イベントオブジェクトを移送します。 * * **注意:**`keydown`はSafari 2.xでクラッシュを引き起こします。 * * @param {HTMLElement} target 指定されたイベントのターゲット。 * @param {Object} options イベント挿入オプションすべてを保持するObjectオブジェクト。 * @param {String} options.type 発火するイベントのタイプ。次のどれであっても構いません。`keyup`、`keydown`、`keypress`。 * @param {Boolean} [options.bubbles=true] イベントをバブリングできる場合は`true`。DOM Level 3では、すべてのキーイベントがデフォルトでバブリングされます。 * @param {Boolean} [options.cancelable=true] `preventDefault`を使用してイベントをキャンセルできる場合は`true`。DOM Level 3では、すべてのキーイベントがキャンセル可能となっています。 * @param {Boolean} [options.ctrlKey=false] イベント発火時にCTRLキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.altKey=false] イベント発火時にALTキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.shiftKey=false] イベント発火時にSHIFTキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.metaKey=false] イベント発火時にMETAキーのいずれかが押される場合は`true`。 * @param {int} [options.keyCode=0] 使用中のキーのコード。 * @param {int} [options.charCode=0] 使用中のキーに関連付けられている文字のUnicodeコード。 * @param {Window} [view=window] ターゲットを保持するビュー。通常はウィンドウオブジェクトです。 * @private */ injectKeyEvent: function (target, options, view) { var type = options.type, customEvent = null; if (type === 'textevent') { type = 'keypress'; } view = view || window; //check for DOM-compliant browsers first if (doc.createEvent){ try { customEvent = doc.createEvent("KeyEvents"); // Interesting problem: Firefox implemented a non-standard // version of initKeyEvent() based on DOM Level 2 specs. // Key event was removed from DOM Level 2 and re-introduced // in DOM Level 3 with a different interface. Firefox is the // only browser with any implementation of Key Events, so for // now, assume it's Firefox if the above line doesn't error. // @TODO: Decipher between Firefox's implementation and a correct one. customEvent.initKeyEvent(type, options.bubbles, options.cancelable, view, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode, options.charCode); } catch (ex) { // If it got here, that means key events aren't officially supported. // Safari/WebKit is a real problem now. WebKit 522 won't let you // set keyCode, charCode, or other properties if you use a // UIEvent, so we first must try to create a generic event. The // fun part is that this will throw an error on Safari 2.x. The // end result is that we need another try...catch statement just to // deal with this mess. try { //try to create generic event - will fail in Safari 2.x customEvent = doc.createEvent("Events"); } catch (uierror) { //the above failed, so create a UIEvent for Safari 2.x customEvent = doc.createEvent("UIEvents"); } finally { customEvent.initEvent(type, options.bubbles, options.cancelable); customEvent.view = view; customEvent.altKey = options.altKey; customEvent.ctrlKey = options.ctrlKey; customEvent.shiftKey = options.shiftKey; customEvent.metaKey = options.metaKey; customEvent.keyCode = options.keyCode; customEvent.charCode = options.charCode; } } target.dispatchEvent(customEvent); } else if (doc.createEventObject) { //IE customEvent = doc.createEventObject(); customEvent.bubbles = options.bubbles; customEvent.cancelable = options.cancelable; customEvent.view = view; customEvent.ctrlKey = options.ctrlKey; customEvent.altKey = options.altKey; customEvent.shiftKey = options.shiftKey; customEvent.metaKey = options.metaKey; // IE doesn't support charCode explicitly. CharCode should // take precedence over any keyCode value for accurate // representation. customEvent.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode; target.fireEvent("on" + type, customEvent); } else { return false; } return true; }, /* * 指定されたイベント情報を使用してマウスイベントを挿入し、イベントオブジェクトを移送します。 * * @param {HTMLElement} target 指定されたイベントのターゲット。 * @param {Object} options イベント挿入オプションすべてを保持するObjectオブジェクト。 * @param {String} options.type 発火するイベントのタイプ。次のどれであっても構いません。`click`、`dblclick`、`mousedown`、`mouseup`、`mouseout`、`mouseover`、`mousemove`。 * @param {Boolean} [options.bubbles=true] イベントをバブリングできる場合は`true`。DOM Level 2では、すべてのマウスイベントがデフォルトでバブリングされます。 * @param {Boolean} [options.cancelable=true] `preventDefault`を使用してイベントをキャンセルできる場合は`true`。DOM Level 2では、`mousemove`を除くすべてのマウスイベントがキャンセル可能となっています。`mousemove`のデフォルト値として`false`が設定されます。 * @param {Boolean} [options.ctrlKey=false] イベント発火時にCTRLキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.altKey=false] イベント発火時にALTキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.shiftKey=false] イベント発火時にSHIFTキーのいずれかが押される場合は`true`。 * @param {Boolean} [options.metaKey=false] イベント発火時にMETAキーのいずれかが押される場合は`true`。 * @param {int} [options.detail=1] マウスボタンが使用された回数。 * @param {int} [options.screenX=0] イベントが発生した画面の場所を示すX座標。 * @param {int} [options.screenY=0] イベントが発生した画面の場所を示すY座標。 * @param {int} [options.clientX=0] イベントが発生したクライアントの場所を示すX座標。 * @param {int} [options.clientY=0] イベントが発生したクライアントの場所を示すY座標。 * @param {int} [options.button=0] イベント実行時に押されていたボタン。値は、0が主マウスボタン(通常は左ボタン)、1がマウスの第3ボタン(通常は中央ボタン)、2がマウスの第2ボタン(通常は右ボタン)です。 * @param {HTMLElement} [options.relatedTarget=null] `mouseout`イベントにおいては、これがマウスの移動先の要素です。`mouseover`イベントにおいては、これがマウスの移動元の要素です。この引数は、その他すべてのイベントにおいては無視されます。 * @param {Window} [view=window] ターゲットを保持するビュー。通常はウィンドウオブジェクトです。 * @private */ injectMouseEvent: function (target, options, view) { var type = options.type, customEvent = null; view = view || window; //check for DOM-compliant browsers first if (doc.createEvent){ customEvent = doc.createEvent("MouseEvents"); //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent() if (customEvent.initMouseEvent){ customEvent.initMouseEvent(type, options.bubbles, options.cancelable, view, options.detail, options.screenX, options.screenY, options.clientX, options.clientY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, options.relatedTarget); } else { //Safari //the closest thing available in Safari 2.x is UIEvents customEvent = doc.createEvent("UIEvents"); customEvent.initEvent(type, options.bubbles, options.cancelable); customEvent.view = view; customEvent.detail = options.detail; customEvent.screenX = options.screenX; customEvent.screenY = options.screenY; customEvent.clientX = options.clientX; customEvent.clientY = options.clientY; customEvent.ctrlKey = options.ctrlKey; customEvent.altKey = options.altKey; customEvent.metaKey = options.metaKey; customEvent.shiftKey = options.shiftKey; customEvent.button = options.button; customEvent.relatedTarget = options.relatedTarget; } /* * relatedTargetが割り当てられているかどうかを確認します。Firefox 2.0より前のバージョンではinitMouseEvent()を使用した割り当てが行えず、このプロパティはイベント作成後に読み取り専用となります。このため、YAHOO.util.getRelatedTarget()が機能するようにするには、IEプロパティに対し、mouseoutイベントについてはtoElementプロパティを、mouseoverイベントについてはfromElementプロパティを割り当てます。 */ if (options.relatedTarget && !customEvent.relatedTarget){ if (type == "mouseout"){ customEvent.toElement = options.relatedTarget; } else if (type == "mouseover"){ customEvent.fromElement = options.relatedTarget; } } target.dispatchEvent(customEvent); } else if (doc.createEventObject) { //IE customEvent = doc.createEventObject(); customEvent.bubbles = options.bubbles; customEvent.cancelable = options.cancelable; customEvent.view = view; customEvent.detail = options.detail; customEvent.screenX = options.screenX; customEvent.screenY = options.screenY; customEvent.clientX = options.clientX; customEvent.clientY = options.clientY; customEvent.ctrlKey = options.ctrlKey; customEvent.altKey = options.altKey; customEvent.metaKey = options.metaKey; customEvent.shiftKey = options.shiftKey; customEvent.button = Player.ieButtonCodeMap[options.button] || 0; /* * IEでは汎用イベントにおけるtoElementやfromElementの割り当てを許可していないため、relatedTargetを使用することが必要です。これにより、YAHOO.util.customEvent.getRelatedTarget()が機能します。 */ customEvent.relatedTarget = options.relatedTarget; target.fireEvent('on' + type, customEvent); } else { return false; } return true; }, /* * 指定されたイベント情報を使用してUIイベントを挿入し、イベントオブジェクトを移送します。 * * @param {HTMLElement} target 指定されたイベントのターゲット。 * @param {String} options.type 発火するイベントのタイプ。次のどれであっても構いません。`click`、`dblclick`、`mousedown`、`mouseup`、`mouseout`、`mouseover`、`mousemove`。 * @param {Boolean} [options.bubbles=true] イベントをバブリングできる場合は`true`。DOM Level 2では、すべてのマウスイベントがデフォルトでバブリングされます。 * @param {Boolean} [options.cancelable=true] `preventDefault`を使用してイベントをキャンセルできる場合は`true`。DOM Level 2では、`mousemove`を除くすべてのマウスイベントがキャンセル可能となっています。`mousemove`のデフォルト値として`false`が設定されます。 * @param {int} [options.detail=1] マウスボタンが使用された回数。 * @param {Window} [view=window] ターゲットを保持するビュー。通常はウィンドウオブジェクトです。 * @private */ injectUIEvent: function (target, options, view) { var customEvent = null; view = view || window; //check for DOM-compliant browsers first if (doc.createEvent){ //just a generic UI Event object is needed customEvent = doc.createEvent("UIEvents"); customEvent.initUIEvent(options.type, options.bubbles, options.cancelable, view, options.detail); target.dispatchEvent(customEvent); } else if (doc.createEventObject){ //IE customEvent = doc.createEventObject(); customEvent.bubbles = options.bubbles; customEvent.cancelable = options.cancelable; customEvent.view = view; customEvent.detail = options.detail; target.fireEvent("on" + options.type, customEvent); } else { return false; } return true; } } // statics }; });