/* ***** 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(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]; } }; if (document.documentElement.setCapture) { exports.capture = function(el, eventHandler, releaseCaptureHandler) { var called = false; function onReleaseCapture(e) { eventHandler(e); if (!called) { called = true; releaseCaptureHandler(e); } exports.removeListener(el, "mousemove", eventHandler); exports.removeListener(el, "mouseup", onReleaseCapture); exports.removeListener(el, "losecapture", onReleaseCapture); el.releaseCapture(); } exports.addListener(el, "mousemove", eventHandler); exports.addListener(el, "mouseup", onReleaseCapture); exports.addListener(el, "losecapture", onReleaseCapture); el.setCapture(); }; } else { exports.capture = function(el, eventHandler, releaseCaptureHandler) { function onMouseUp(e) { eventHandler && eventHandler(e); releaseCaptureHandler && releaseCaptureHandler(e); document.removeEventListener("mousemove", eventHandler, true); document.removeEventListener("mouseup", onMouseUp, true); e.stopPropagation(); } document.addEventListener("mousemove", eventHandler, true); document.addEventListener("mouseup", onMouseUp, true); }; } exports.addMouseWheelListener = function(el, callback) { var factor = 8; var listener = function(e) { if (e.wheelDelta !== undefined) { if (e.wheelDeltaX !== undefined) { e.wheelX = -e.wheelDeltaX / factor; e.wheelY = -e.wheelDeltaY / factor; } else { e.wheelX = 0; e.wheelY = -e.wheelDelta / factor; } } else { 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.addListener(el, "DOMMouseScroll", listener); exports.addListener(el, "mousewheel", listener); }; 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 { var isNewClick = Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5; if (!timer || isNewClick) clicks = 0; clicks += 1; if (timer) clearTimeout(timer) timer = setTimeout(function() {timer = null}, timeouts[clicks - 1] || 600); } 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 (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 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); } 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 lastDown = null; addListener(el, "keydown", function(e) { lastDown = e.keyIdentifier || e.keyCode; return normalizeCommandKeys(callback, e, e.keyCode); }); } }; 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.oRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame; if (exports.nextFrame) exports.nextFrame = exports.nextFrame.bind(window); else exports.nextFrame = function(callback) { setTimeout(callback, 17); }; });