/* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2010, Ajax.org B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Ajax.org B.V. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { "use strict"; var keys = require("./keys"); var useragent = require("./useragent"); var dom = require("./dom"); exports.addListener = function(elem, type, callback) { if (elem.addEventListener) { return elem.addEventListener(type, callback, false); } if (elem.attachEvent) { var wrapper = function() { callback.call(elem, window.event); }; callback._wrapper = wrapper; elem.attachEvent("on" + type, wrapper); } }; exports.removeListener = function(elem, type, callback) { if (elem.removeEventListener) { return elem.removeEventListener(type, callback, false); } if (elem.detachEvent) { elem.detachEvent("on" + type, callback._wrapper || callback); } }; /* * Prevents propagation and clobbers the default action of the passed event */ exports.stopEvent = function(e) { exports.stopPropagation(e); exports.preventDefault(e); return false; }; exports.stopPropagation = function(e) { if (e.stopPropagation) e.stopPropagation(); else e.cancelBubble = true; }; exports.preventDefault = function(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; }; /* * @return {Number} 0 for left button, 1 for middle button, 2 for right button */ exports.getButton = function(e) { if (e.type == "dblclick") return 0; if (e.type == "contextmenu" || (e.ctrlKey && useragent.isMac)) return 2; // DOM Event if (e.preventDefault) { return e.button; } // old IE else { return {1:0, 2:2, 4:1}[e.button]; } }; exports.capture = function(el, eventHandler, releaseCaptureHandler) { function onMouseUp(e) { eventHandler && eventHandler(e); releaseCaptureHandler && releaseCaptureHandler(e); exports.removeListener(document, "mousemove", eventHandler, true); exports.removeListener(document, "mouseup", onMouseUp, true); exports.removeListener(document, "dragstart", onMouseUp, true); } exports.addListener(document, "mousemove", eventHandler, true); exports.addListener(document, "mouseup", onMouseUp, true); exports.addListener(document, "dragstart", onMouseUp, true); return onMouseUp; }; exports.addMouseWheelListener = function(el, callback) { if ("onmousewheel" in el) { var factor = 8; exports.addListener(el, "mousewheel", function(e) { if (e.wheelDeltaX !== undefined) { e.wheelX = -e.wheelDeltaX / factor; e.wheelY = -e.wheelDeltaY / factor; } else { e.wheelX = 0; e.wheelY = -e.wheelDelta / factor; } callback(e); }); } else if ("onwheel" in el) { exports.addListener(el, "wheel", function(e) { e.wheelX = (e.deltaX || 0) * 5; e.wheelY = (e.deltaY || 0) * 5; callback(e); }); } else { exports.addListener(el, "DOMMouseScroll", function(e) { if (e.axis && e.axis == e.HORIZONTAL_AXIS) { e.wheelX = (e.detail || 0) * 5; e.wheelY = 0; } else { e.wheelX = 0; e.wheelY = (e.detail || 0) * 5; } callback(e); }); } }; exports.addMultiMouseDownListener = function(el, timeouts, eventHandler, callbackName) { var clicks = 0; var startX, startY, timer; var eventNames = { 2: "dblclick", 3: "tripleclick", 4: "quadclick" }; exports.addListener(el, "mousedown", function(e) { if (exports.getButton(e) != 0) { clicks = 0; } else if (e.detail > 1) { clicks++; if (clicks > 4) clicks = 1; } else { clicks = 1; } if (useragent.isIE) { var isNewClick = Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5; if (isNewClick) { clicks = 1; } if (clicks == 1) { startX = e.clientX; startY = e.clientY; } } eventHandler[callbackName]("mousedown", e); if (clicks > 4) clicks = 0; else if (clicks > 1) return eventHandler[callbackName](eventNames[clicks], e); }); if (useragent.isOldIE) { exports.addListener(el, "dblclick", function(e) { clicks = 2; if (timer) clearTimeout(timer); timer = setTimeout(function() {timer = null}, timeouts[clicks - 1] || 600); eventHandler[callbackName]("mousedown", e); eventHandler[callbackName](eventNames[clicks], e); }); } }; function normalizeCommandKeys(callback, e, keyCode) { var hashId = 0; if ((useragent.isOpera && !("KeyboardEvent" in window)) && useragent.isMac) { hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0); } else { hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); } if (!useragent.isMac && pressedKeys) { if (pressedKeys[91] || pressedKeys[92]) hashId |= 8; if (pressedKeys.altGr) { if ((3 & hashId) != 3) pressedKeys.altGr = 0 else return; } if (keyCode === 18 || keyCode === 17) { var location = e.location || e.keyLocation; if (keyCode === 17 && location === 1) { ts = e.timeStamp; } else if (keyCode === 18 && hashId === 3 && location === 2) { var dt = -ts; ts = e.timeStamp; dt += ts; if (dt < 3) pressedKeys.altGr = true; } } } if (keyCode in keys.MODIFIER_KEYS) { switch (keys.MODIFIER_KEYS[keyCode]) { case "Alt": hashId = 2; break; case "Shift": hashId = 4; break; case "Ctrl": hashId = 1; break; default: hashId = 8; break; } keyCode = 0; } if (hashId & 8 && (keyCode === 91 || keyCode === 93)) { keyCode = 0; } if (!hashId && keyCode === 13) { if (e.location || e.keyLocation === 3) { callback(e, hashId, -keyCode) if (e.defaultPrevented) return; } } // If there is no hashId and the keyCode is not a function key, then // we don't call the callback as we don't handle a command key here // (it's a normal key/character input). if (!hashId && !(keyCode in keys.FUNCTION_KEYS) && !(keyCode in keys.PRINTABLE_KEYS)) { return false; } return callback(e, hashId, keyCode); } var pressedKeys = null; var ts = 0; exports.addCommandKeyListener = function(el, callback) { var addListener = exports.addListener; if (useragent.isOldGecko || (useragent.isOpera && !("KeyboardEvent" in window))) { // Old versions of Gecko aka. Firefox < 4.0 didn't repeat the keydown // event if the user pressed the key for a longer time. Instead, the // keydown event was fired once and later on only the keypress event. // To emulate the 'right' keydown behavior, the keyCode of the initial // keyDown event is stored and in the following keypress events the // stores keyCode is used to emulate a keyDown event. var lastKeyDownKeyCode = null; addListener(el, "keydown", function(e) { lastKeyDownKeyCode = e.keyCode; }); addListener(el, "keypress", function(e) { return normalizeCommandKeys(callback, e, lastKeyDownKeyCode); }); } else { var lastDefaultPrevented = null; addListener(el, "keydown", function(e) { pressedKeys[e.keyCode] = true; var result = normalizeCommandKeys(callback, e, e.keyCode); lastDefaultPrevented = e.defaultPrevented; return result; }); addListener(el, "keypress", function(e) { if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { exports.stopEvent(e); lastDefaultPrevented = null; } }); addListener(el, "keyup", function(e) { pressedKeys[e.keyCode] = null; }); if (!pressedKeys) { pressedKeys = Object.create(null); addListener(window, "focus", function(e) { pressedKeys = Object.create(null); }); } } }; if (window.postMessage && !useragent.isOldIE) { var postMessageId = 1; exports.nextTick = function(callback, win) { win = win || window; var messageName = "zero-timeout-message-" + postMessageId; exports.addListener(win, "message", function listener(e) { if (e.data == messageName) { exports.stopPropagation(e); exports.removeListener(win, "message", listener); callback(); } }); win.postMessage(messageName, "*"); }; } exports.nextFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame; if (exports.nextFrame) exports.nextFrame = exports.nextFrame.bind(window); else exports.nextFrame = function(callback) { setTimeout(callback, 17); }; });