alloyfinger
Version:
super tiny size multi-touch gestures library for the web.
1,161 lines (1,125 loc) • 171 kB
JavaScript
/* Alloy Game Engine
* By AlloyTeam http://www.alloyteam.com/
* Github: https://github.com/AlloyTeam/AlloyGameEngine
* MIT Licensed.
*/
;(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.AlloyPaper = factory();
}
}(this, function () {
'use strict';
// The base Class implementation (does nothing)
var Class = function () { };
// Create a new Class that inherits from this class
Class.extend = function (prop) {
var _super = this.prototype;
var prototype = Object.create(_super);
// Copy the properties over onto the new prototype
for (var name in prop) {
if (name != "statics") {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" ?
(function (temp_name, fn) {
return function () {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[temp_name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
}
// The dummy class constructor
function _Class() {
// All construction is actually done in the init method
this.ctor.apply(this, arguments);
}
//继承父类的静态属性
for (var key in this) {
if (this.hasOwnProperty(key) && key != "extend")
_Class[key] = this[key];
}
// Populate our constructed prototype object
_Class.prototype = prototype;
_Class.prototype._super = Object.create(_super);
//静态属性和方法
if (prop.statics) {
for (var key in prop.statics) {
if (prop.statics.hasOwnProperty(key)) {
_Class[key] = prop.statics[key];
if (key == "ctor") {
//提前执行静态构造函数
_Class[key]();
}
}
}
}
// Enforce the constructor to be what we expect
_Class.prototype.constructor = _Class;
// And make this class extendable
_Class.extend = Class.extend;
return _Class;
};
window.Class = Class;
//AlloyPaper
var AlloyPaper={};
AlloyPaper.DefaultCursor = "default";
AlloyPaper.Cache = {};
AlloyPaper.TWEEN = Class.extend({
"statics": {
"ctor": function() {
if (Date.now === undefined) {
Date.now = function() {
return new Date().valueOf();
};
}
this._tweens = [];
},
"REVISION": "14",
"getAll": function() {
return this._tweens;
},
"removeAll": function() {
this._tweens = [];
},
"add": function(tween) {
this._tweens.push(tween);
},
"remove": function(tween) {
var i = this._tweens.indexOf(tween);
if (i !== -1) {
this._tweens.splice(i, 1);
}
},
"update": function(time) {
if (this._tweens.length === 0) return false;
var i = 0;
time = time !== undefined ? time : typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
while (i < this._tweens.length) {
if (this._tweens[i].update(time)) {
i++;
} else {
this._tweens.splice(i, 1);
}
}
return true;
},
"Tween": function(object) {
var _object = object;
var _valuesStart = {};
var _valuesEnd = {};
var _valuesStartRepeat = {};
var _duration = 1e3;
var _repeat = 0;
var _yoyo = false;
var _isPlaying = false;
var _reversed = false;
var _delayTime = 0;
var _startTime = null;
var _easingFunction = AlloyPaper.TWEEN.Easing.Linear.None;
var _interpolationFunction = AlloyPaper.TWEEN.Interpolation.Linear;
var _chainedTweens = [];
var _onStartCallback = null;
var _onStartCallbackFired = false;
var _onUpdateCallback = null;
var _onCompleteCallback = null;
var _onStopCallback = null;
var _paused = false,
_passTime = null;
for (var field in object) {
_valuesStart[field] = parseFloat(object[field], 10);
}
this.toggle = function() {
if (_paused) {
this.play();
} else {
this.pause();
}
};
this.pause = function() {
_paused = true;
var pauseTime = typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
_passTime = pauseTime - _startTime;
};
this.play = function() {
_paused = false;
var nowTime = typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
_startTime = nowTime - _passTime;
};
this.to = function(properties, duration) {
if (duration !== undefined) {
_duration = duration;
}
_valuesEnd = properties;
return this;
};
this.start = function(time) {
AlloyPaper.TWEEN.add(this);
_isPlaying = true;
_onStartCallbackFired = false;
_startTime = time !== undefined ? time : typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
_startTime += _delayTime;
for (var property in _valuesEnd) {
if (_valuesEnd[property] instanceof Array) {
if (_valuesEnd[property].length === 0) {
continue;
}
_valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);
}
_valuesStart[property] = _object[property];
if (_valuesStart[property] instanceof Array === false) {
_valuesStart[property] *= 1;
}
_valuesStartRepeat[property] = _valuesStart[property] || 0;
}
return this;
};
this.stop = function() {
if (!_isPlaying) {
return this;
}
AlloyPaper.TWEEN.remove(this);
_isPlaying = false;
if (_onStopCallback !== null) {
_onStopCallback.call(_object);
}
this.stopChainedTweens();
return this;
};
this.stopChainedTweens = function() {
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
_chainedTweens[i].stop();
}
};
this.delay = function(amount) {
_delayTime = amount;
return this;
};
this.repeat = function(times) {
_repeat = times;
return this;
};
this.yoyo = function(yoyo) {
_yoyo = yoyo;
return this;
};
this.easing = function(easing) {
_easingFunction = easing;
return this;
};
this.interpolation = function(interpolation) {
_interpolationFunction = interpolation;
return this;
};
this.chain = function() {
_chainedTweens = arguments;
return this;
};
this.onStart = function(callback) {
_onStartCallback = callback;
return this;
};
this.onUpdate = function(callback) {
_onUpdateCallback = callback;
return this;
};
this.onComplete = function(callback) {
_onCompleteCallback = callback;
return this;
};
this.onStop = function(callback) {
_onStopCallback = callback;
return this;
};
this.update = function(time) {
if (_paused) return true;
var property;
if (time < _startTime) {
return true;
}
if (_onStartCallbackFired === false) {
if (_onStartCallback !== null) {
_onStartCallback.call(_object);
}
_onStartCallbackFired = true;
}
var elapsed = (time - _startTime) / _duration;
elapsed = elapsed > 1 ? 1 : elapsed;
var value = _easingFunction(elapsed);
for (property in _valuesEnd) {
var start = _valuesStart[property] || 0;
var end = _valuesEnd[property];
if (end instanceof Array) {
_object[property] = _interpolationFunction(end, value);
} else {
if (typeof end === "string") {
end = start + parseFloat(end, 10);
}
if (typeof end === "number") {
_object[property] = start + (end - start) * value;
}
}
}
if (_onUpdateCallback !== null) {
_onUpdateCallback.call(_object, value);
}
if (elapsed == 1) {
if (_repeat > 0) {
if (isFinite(_repeat)) {
_repeat--;
}
for (property in _valuesStartRepeat) {
if (typeof _valuesEnd[property] === "string") {
_valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property], 10);
}
if (_yoyo) {
var tmp = _valuesStartRepeat[property];
_valuesStartRepeat[property] = _valuesEnd[property];
_valuesEnd[property] = tmp;
}
_valuesStart[property] = _valuesStartRepeat[property];
}
if (_yoyo) {
_reversed = !_reversed;
}
_startTime = time + _delayTime;
return true;
} else {
if (_onCompleteCallback !== null) {
_onCompleteCallback.call(_object);
}
for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
_chainedTweens[i].start(time);
}
return false;
}
}
return true;
};
},
"Easing": {
"Linear": {
"None": function(k) {
return k;
}
},
"Quadratic": {
"In": function(k) {
return k * k;
},
"Out": function(k) {
return k * (2 - k);
},
"InOut": function(k) {
if ((k *= 2) < 1) return.5 * k * k;
return -.5 * (--k * (k - 2) - 1);
}
},
"Cubic": {
"In": function(k) {
return k * k * k;
},
"Out": function(k) {
return --k * k * k + 1;
},
"InOut": function(k) {
if ((k *= 2) < 1) return.5 * k * k * k;
return.5 * ((k -= 2) * k * k + 2);
}
},
"Quartic": {
"In": function(k) {
return k * k * k * k;
},
"Out": function(k) {
return 1 - --k * k * k * k;
},
"InOut": function(k) {
if ((k *= 2) < 1) return.5 * k * k * k * k;
return -.5 * ((k -= 2) * k * k * k - 2);
}
},
"Quintic": {
"In": function(k) {
return k * k * k * k * k;
},
"Out": function(k) {
return --k * k * k * k * k + 1;
},
"InOut": function(k) {
if ((k *= 2) < 1) return.5 * k * k * k * k * k;
return.5 * ((k -= 2) * k * k * k * k + 2);
}
},
"Sinusoidal": {
"In": function(k) {
return 1 - Math.cos(k * Math.PI / 2);
},
"Out": function(k) {
return Math.sin(k * Math.PI / 2);
},
"InOut": function(k) {
return.5 * (1 - Math.cos(Math.PI * k));
}
},
"Exponential": {
"In": function(k) {
return k === 0 ? 0 : Math.pow(1024, k - 1);
},
"Out": function(k) {
return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
},
"InOut": function(k) {
if (k === 0) return 0;
if (k === 1) return 1;
if ((k *= 2) < 1) return.5 * Math.pow(1024, k - 1);
return.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
}
},
"Circular": {
"In": function(k) {
return 1 - Math.sqrt(1 - k * k);
},
"Out": function(k) {
return Math.sqrt(1 - --k * k);
},
"InOut": function(k) {
if ((k *= 2) < 1) return -.5 * (Math.sqrt(1 - k * k) - 1);
return.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
}
},
"Elastic": {
"In": function(k) {
var s, a = .1,
p = .4;
if (k === 0) return 0;
if (k === 1) return 1;
if (!a || a < 1) {
a = 1;
s = p / 4;
} else s = p * Math.asin(1 / a) / (2 * Math.PI);
return -(a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
},
"Out": function(k) {
var s, a = .1,
p = .4;
if (k === 0) return 0;
if (k === 1) return 1;
if (!a || a < 1) {
a = 1;
s = p / 4;
} else s = p * Math.asin(1 / a) / (2 * Math.PI);
return a * Math.pow(2, -10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1;
},
"InOut": function(k) {
var s, a = .1,
p = .4;
if (k === 0) return 0;
if (k === 1) return 1;
if (!a || a < 1) {
a = 1;
s = p / 4;
} else s = p * Math.asin(1 / a) / (2 * Math.PI);
if ((k *= 2) < 1) return -.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * .5 + 1;
}
},
"Back": {
"In": function(k) {
var s = 1.70158;
return k * k * ((s + 1) * k - s);
},
"Out": function(k) {
var s = 1.70158;
return --k * k * ((s + 1) * k + s) + 1;
},
"InOut": function(k) {
var s = 1.70158 * 1.525;
if ((k *= 2) < 1) return.5 * (k * k * ((s + 1) * k - s));
return.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
}
},
"Bounce": {
"In": function(k) {
return 1 - AlloyPaper.TWEEN.Easing.Bounce.Out(1 - k);
},
"Out": function(k) {
if (k < 1 / 2.75) {
return 7.5625 * k * k;
} else if (k < 2 / 2.75) {
return 7.5625 * (k -= 1.5 / 2.75) * k + .75;
} else if (k < 2.5 / 2.75) {
return 7.5625 * (k -= 2.25 / 2.75) * k + .9375;
} else {
return 7.5625 * (k -= 2.625 / 2.75) * k + .984375;
}
},
"InOut": function(k) {
if (k < .5) return AlloyPaper.TWEEN.Easing.Bounce.In(k * 2) * .5;
return AlloyPaper.TWEEN.Easing.Bounce.Out(k * 2 - 1) * .5 + .5;
}
}
},
"Interpolation": {
"Linear": function(v, k) {
var m = v.length - 1,
f = m * k,
i = Math.floor(f),
fn = AlloyPaper.TWEEN.Interpolation.Utils.Linear;
if (k < 0) return fn(v[0], v[1], f);
if (k > 1) return fn(v[m], v[m - 1], m - f);
return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
},
"Bezier": function(v, k) {
var b = 0,
n = v.length - 1,
pw = Math.pow,
bn = AlloyPaper.TWEEN.Interpolation.Utils.Bernstein,
i;
for (i = 0; i <= n; i++) {
b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
}
return b;
},
"CatmullRom": function(v, k) {
var m = v.length - 1,
f = m * k,
i = Math.floor(f),
fn = AlloyPaper.TWEEN.Interpolation.Utils.CatmullRom;
if (v[0] === v[m]) {
if (k < 0) i = Math.floor(f = m * (1 + k));
return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
} else {
if (k < 0) return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
if (k > 1) return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
}
},
"Utils": {
"Linear": function(p0, p1, t) {
return (p1 - p0) * t + p0;
},
"Bernstein": function(n, i) {
var fc = AlloyPaper.TWEEN.Interpolation.Utils.getFactorial();
return fc(n) / fc(i) / fc(n - i);
},
"getFactorial": function() {
return function() {
var a = [1];
return function(n) {
var s = 1,
i;
if (a[n]) return a[n];
for (i = n; i > 1; i--) s *= i;
return a[n] = s;
};
}();
},
"CatmullRom": function(p0, p1, p2, p3, t) {
var v0 = (p2 - p0) * .5,
v1 = (p3 - p1) * .5,
t2 = t * t,
t3 = t * t2;
return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
}
}
}
}
});
//begin-------------------AlloyPaper.Dom---------------------begin
AlloyPaper.Dom = Class.extend({
"statics": {
"get": function(selector) {
this.element = document.querySelector(selector);
return this;
},
"on": function(type, fn) {
this.element.addEventListener(type, fn, false);
return this;
}
}
});
//end-------------------AlloyPaper.Dom---------------------end
//begin-------------------AlloyPaper.FPS---------------------begin
AlloyPaper.FPS = Class.extend({
"statics": {
"get": function() {
if (!this.instance) this.instance = new this();
this.instance._computeFPS();
return this.instance;
}
},
"ctor": function() {
this.last = new Date();
this.current = null;
this.lastMeasured=new Date();
this.fpsList = [];
this.totalValue = 0;
this.value = 60;
},
"_computeFPS": function() {
this.current = new Date();
if (this.current - this.last > 0) {
var fps = Math.ceil(1e3 / (this.current - this.last));
this.fpsList.push(fps);
this.totalValue += fps;
this.last = this.current;
}
if (this.current - this.lastMeasured > 1000) {
this.value =Math.ceil( this.totalValue / this.fpsList.length);
this.totalValue = 0;
this.fpsList.length = 0;
this.lastMeasured = this.current;
}
}
});
//end-------------------AlloyPaper.FPS---------------------end
AlloyPaper.Keyboard = Class.extend({
"statics": {
"ctor": function() {
var KeyboardJS = {},
locales = {},
locale, map, macros, activeKeys = [],
bindings = [],
activeBindings = [],
activeMacros = [],
aI, usLocale;
usLocale = {
map: {
"3": ["cancel"],
"8": ["backspace"],
"9": ["tab"],
"12": ["clear"],
"13": ["enter"],
"16": ["shift"],
"17": ["ctrl"],
"18": ["alt", "menu"],
"19": ["pause", "break"],
"20": ["capslock"],
"27": ["escape", "esc"],
"32": ["space", "spacebar"],
"33": ["pageup"],
"34": ["pagedown"],
"35": ["end"],
"36": ["home"],
"37": ["left"],
"38": ["up"],
"39": ["right"],
"40": ["down"],
"41": ["select"],
"42": ["printscreen"],
"43": ["execute"],
"44": ["snapshot"],
"45": ["insert", "ins"],
"46": ["delete", "del"],
"47": ["help"],
"91": ["command", "windows", "win", "super", "leftcommand", "leftwindows", "leftwin", "leftsuper"],
"92": ["command", "windows", "win", "super", "rightcommand", "rightwindows", "rightwin", "rightsuper"],
"145": ["scrolllock", "scroll"],
"186": ["semicolon", ";"],
"187": ["equal", "equalsign", "="],
"188": ["comma", ","],
"189": ["dash", "-"],
"190": ["period", "."],
"191": ["slash", "forwardslash", "/"],
"192": ["graveaccent", "`"],
"219": ["openbracket", "["],
"220": ["backslash", "\\"],
"221": ["closebracket", "]"],
"222": ["apostrophe", "'"],
"48": ["zero", "0"],
"49": ["one", "1"],
"50": ["two", "2"],
"51": ["three", "3"],
"52": ["four", "4"],
"53": ["five", "5"],
"54": ["six", "6"],
"55": ["seven", "7"],
"56": ["eight", "8"],
"57": ["nine", "9"],
"96": ["numzero", "num0"],
"97": ["numone", "num1"],
"98": ["numtwo", "num2"],
"99": ["numthree", "num3"],
"100": ["numfour", "num4"],
"101": ["numfive", "num5"],
"102": ["numsix", "num6"],
"103": ["numseven", "num7"],
"104": ["numeight", "num8"],
"105": ["numnine", "num9"],
"106": ["nummultiply", "num*"],
"107": ["numadd", "num+"],
"108": ["numenter"],
"109": ["numsubtract", "num-"],
"110": ["numdecimal", "num."],
"111": ["numdivide", "num/"],
"144": ["numlock", "num"],
"112": ["f1"],
"113": ["f2"],
"114": ["f3"],
"115": ["f4"],
"116": ["f5"],
"117": ["f6"],
"118": ["f7"],
"119": ["f8"],
"120": ["f9"],
"121": ["f10"],
"122": ["f11"],
"123": ["f12"]
},
macros: [["shift + `", ["tilde", "~"]], ["shift + 1", ["exclamation", "exclamationpoint", "!"]], ["shift + 2", ["at", "@"]], ["shift + 3", ["number", "#"]], ["shift + 4", ["dollar", "dollars", "dollarsign", "$"]], ["shift + 5", ["percent", "%"]], ["shift + 6", ["caret", "^"]], ["shift + 7", ["ampersand", "and", "&"]], ["shift + 8", ["asterisk", "*"]], ["shift + 9", ["openparen", "("]], ["shift + 0", ["closeparen", ")"]], ["shift + -", ["underscore", "_"]], ["shift + =", ["plus", "+"]], ["shift + (", ["opencurlybrace", "opencurlybracket", "{"]], ["shift + )", ["closecurlybrace", "closecurlybracket", "}"]], ["shift + \\", ["verticalbar", "|"]], ["shift + ;", ["colon", ":"]], ["shift + '", ["quotationmark", '"']], ["shift + !,", ["openanglebracket", "<"]], ["shift + .", ["closeanglebracket", ">"]], ["shift + /", ["questionmark", "?"]]]
};
for (aI = 65; aI <= 90; aI += 1) {
usLocale.map[aI] = String.fromCharCode(aI + 32);
usLocale.macros.push(["shift + " + String.fromCharCode(aI + 32) + ", capslock + " + String.fromCharCode(aI + 32), [String.fromCharCode(aI)]]);
}
registerLocale("us", usLocale);
getSetLocale("us");
enable();
KeyboardJS.enable = enable;
KeyboardJS.disable = disable;
KeyboardJS.activeKeys = getActiveKeys;
KeyboardJS.releaseKey = removeActiveKey;
KeyboardJS.pressKey = addActiveKey;
KeyboardJS.on = createBinding;
KeyboardJS.clear = removeBindingByKeyCombo;
KeyboardJS.clear.key = removeBindingByKeyName;
KeyboardJS.locale = getSetLocale;
KeyboardJS.locale.register = registerLocale;
KeyboardJS.macro = createMacro;
KeyboardJS.macro.remove = removeMacro;
KeyboardJS.key = {};
KeyboardJS.key.name = getKeyName;
KeyboardJS.key.code = getKeyCode;
KeyboardJS.combo = {};
KeyboardJS.combo.active = isSatisfiedCombo;
KeyboardJS.combo.parse = parseKeyCombo;
KeyboardJS.combo.stringify = stringifyKeyCombo;
function enable() {
if (window.addEventListener) {
window.document.addEventListener("keydown", keydown, false);
window.document.addEventListener("keyup", keyup, false);
window.addEventListener("blur", reset, false);
window.addEventListener("webkitfullscreenchange", reset, false);
window.addEventListener("mozfullscreenchange", reset, false);
} else if (window.attachEvent) {
window.document.attachEvent("onkeydown", keydown);
window.document.attachEvent("onkeyup", keyup);
window.attachEvent("onblur", reset);
}
}
function disable() {
reset();
if (window.removeEventListener) {
window.document.removeEventListener("keydown", keydown, false);
window.document.removeEventListener("keyup", keyup, false);
window.removeEventListener("blur", reset, false);
window.removeEventListener("webkitfullscreenchange", reset, false);
window.removeEventListener("mozfullscreenchange", reset, false);
} else if (window.detachEvent) {
window.document.detachEvent("onkeydown", keydown);
window.document.detachEvent("onkeyup", keyup);
window.detachEvent("onblur", reset);
}
}
function reset(event) {
activeKeys = [];
pruneMacros();
pruneBindings(event);
}
function keydown(event) {
var keyNames, keyName, kI;
keyNames = getKeyName(event.keyCode);
if (keyNames.length < 1) {
return;
}
event.isRepeat = false;
for (kI = 0; kI < keyNames.length; kI += 1) {
keyName = keyNames[kI];
if (getActiveKeys().indexOf(keyName) != -1) event.isRepeat = true;
addActiveKey(keyName);
}
executeMacros();
executeBindings(event);
}
function keyup(event) {
var keyNames, kI;
keyNames = getKeyName(event.keyCode);
if (keyNames.length < 1) {
return;
}
for (kI = 0; kI < keyNames.length; kI += 1) {
removeActiveKey(keyNames[kI]);
}
pruneMacros();
pruneBindings(event);
}
function getKeyName(keyCode) {
return map[keyCode] || [];
}
function getKeyCode(keyName) {
var keyCode;
for (keyCode in map) {
if (!map.hasOwnProperty(keyCode)) {
continue;
}
if (map[keyCode].indexOf(keyName) > -1) {
return keyCode;
}
}
return false;
}
function createMacro(combo, injectedKeys) {
if (typeof combo !== "string" && (typeof combo !== "object" || typeof combo.push !== "function")) {
throw new Error("Cannot create macro. The combo must be a string or array.");
}
if (typeof injectedKeys !== "object" || typeof injectedKeys.push !== "function") {
throw new Error("Cannot create macro. The injectedKeys must be an array.");
}
macros.push([combo, injectedKeys]);
}
function removeMacro(combo) {
var macro, mI;
if (typeof combo !== "string" && (typeof combo !== "object" || typeof combo.push !== "function")) {
throw new Error("Cannot remove macro. The combo must be a string or array.");
}
for (mI = 0; mI < macros.length; mI += 1) {
macro = macros[mI];
if (compareCombos(combo, macro[0])) {
removeActiveKey(macro[1]);
macros.splice(mI, 1);
break;
}
}
}
function executeMacros() {
var mI, combo, kI;
for (mI = 0; mI < macros.length; mI += 1) {
combo = parseKeyCombo(macros[mI][0]);
if (activeMacros.indexOf(macros[mI]) === -1 && isSatisfiedCombo(combo)) {
activeMacros.push(macros[mI]);
for (kI = 0; kI < macros[mI][1].length; kI += 1) {
addActiveKey(macros[mI][1][kI]);
}
}
}
}
function pruneMacros() {
var mI, combo, kI;
for (mI = 0; mI < activeMacros.length; mI += 1) {
combo = parseKeyCombo(activeMacros[mI][0]);
if (isSatisfiedCombo(combo) === false) {
for (kI = 0; kI < activeMacros[mI][1].length; kI += 1) {
removeActiveKey(activeMacros[mI][1][kI]);
}
activeMacros.splice(mI, 1);
mI -= 1;
}
}
}
function createBinding(keyCombo, keyDownCallback, keyUpCallback) {
var api = {},
binding, subBindings = [],
bindingApi = {},
kI, subCombo;
if (typeof keyCombo === "string") {
keyCombo = parseKeyCombo(keyCombo);
}
for (kI = 0; kI < keyCombo.length; kI += 1) {
binding = {};
subCombo = stringifyKeyCombo([keyCombo[kI]]);
if (typeof subCombo !== "string") {
throw new Error("Failed to bind key combo. The key combo must be string.");
}
binding.keyCombo = subCombo;
binding.keyDownCallback = [];
binding.keyUpCallback = [];
if (keyDownCallback) {
binding.keyDownCallback.push(keyDownCallback);
}
if (keyUpCallback) {
binding.keyUpCallback.push(keyUpCallback);
}
bindings.push(binding);
subBindings.push(binding);
}
api.clear = clear;
api.on = on;
return api;
function clear() {
var bI;
for (bI = 0; bI < subBindings.length; bI += 1) {
bindings.splice(bindings.indexOf(subBindings[bI]), 1);
}
}
function on(eventName) {
var api = {},
callbacks, cI, bI;
if (typeof eventName !== "string") {
throw new Error("Cannot bind callback. The event name must be a string.");
}
if (eventName !== "keyup" && eventName !== "keydown") {
throw new Error('Cannot bind callback. The event name must be a "keyup" or "keydown".');
}
callbacks = Array.prototype.slice.apply(arguments, [1]);
for (cI = 0; cI < callbacks.length; cI += 1) {
if (typeof callbacks[cI] === "function") {
if (eventName === "keyup") {
for (bI = 0; bI < subBindings.length; bI += 1) {
subBindings[bI].keyUpCallback.push(callbacks[cI]);
}
} else if (eventName === "keydown") {
for (bI = 0; bI < subBindings.length; bI += 1) {
subBindings[bI].keyDownCallback.push(callbacks[cI]);
}
}
}
}
api.clear = clear;
return api;
function clear() {
var cI, bI;
for (cI = 0; cI < callbacks.length; cI += 1) {
if (typeof callbacks[cI] === "function") {
if (eventName === "keyup") {
for (bI = 0; bI < subBindings.length; bI += 1) {
subBindings[bI].keyUpCallback.splice(subBindings[bI].keyUpCallback.indexOf(callbacks[cI]), 1);
}
} else {
for (bI = 0; bI < subBindings.length; bI += 1) {
subBindings[bI].keyDownCallback.splice(subBindings[bI].keyDownCallback.indexOf(callbacks[cI]), 1);
}
}
}
}
}
}
}
function removeBindingByKeyCombo(keyCombo) {
var bI, binding, keyName;
for (bI = 0; bI < bindings.length; bI += 1) {
binding = bindings[bI];
if (compareCombos(keyCombo, binding.keyCombo)) {
bindings.splice(bI, 1);
bI -= 1;
}
}
}
function removeBindingByKeyName(keyName) {
var bI, kI, binding;
if (keyName) {
for (bI = 0; bI < bindings.length; bI += 1) {
binding = bindings[bI];
for (kI = 0; kI < binding.keyCombo.length; kI += 1) {
if (binding.keyCombo[kI].indexOf(keyName) > -1) {
bindings.splice(bI, 1);
bI -= 1;
break;
}
}
}
} else {
bindings = [];
}
}
function executeBindings(event) {
var bI, sBI, binding, bindingKeys, remainingKeys, cI, killEventBubble, kI, bindingKeysSatisfied, index, sortedBindings = [],
bindingWeight;
remainingKeys = [].concat(activeKeys);
for (bI = 0; bI < bindings.length; bI += 1) {
bindingWeight = extractComboKeys(bindings[bI].keyCombo).length;
if (!sortedBindings[bindingWeight]) {
sortedBindings[bindingWeight] = [];
}
sortedBindings[bindingWeight].push(bindings[bI]);
}
for (sBI = sortedBindings.length - 1; sBI >= 0; sBI -= 1) {
if (!sortedBindings[sBI]) {
continue;
}
for (bI = 0; bI < sortedBindings[sBI].length; bI += 1) {
binding = sortedBindings[sBI][bI];
bindingKeys = extractComboKeys(binding.keyCombo);
bindingKeysSatisfied = true;
for (kI = 0; kI < bindingKeys.length; kI += 1) {
if (remainingKeys.indexOf(bindingKeys[kI]) === -1) {
bindingKeysSatisfied = false;
break;
}
}
if (bindingKeysSatisfied && isSatisfiedCombo(binding.keyCombo)) {
activeBindings.push(binding);
for (kI = 0; kI < bindingKeys.length; kI += 1) {
index = remainingKeys.indexOf(bindingKeys[kI]);
if (index > -1) {
remainingKeys.splice(index, 1);
kI -= 1;
}
}
for (cI = 0; cI < binding.keyDownCallback.length; cI += 1) {
if (binding.keyDownCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
killEventBubble = true;
}
}
if (killEventBubble === true) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
function pruneBindings(event) {
var bI, cI, binding, killEventBubble;
for (bI = 0; bI < activeBindings.length; bI += 1) {
binding = activeBindings[bI];
if (isSatisfiedCombo(binding.keyCombo) === false) {
for (cI = 0; cI < binding.keyUpCallback.length; cI += 1) {
if (binding.keyUpCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
killEventBubble = true;
}
}
if (killEventBubble === true) {
event.preventDefault();
event.stopPropagation();
}
activeBindings.splice(bI, 1);
bI -= 1;
}
}
}
function compareCombos(keyComboArrayA, keyComboArrayB) {
var cI, sI, kI;
keyComboArrayA = parseKeyCombo(keyComboArrayA);
keyComboArrayB = parseKeyCombo(keyComboArrayB);
if (keyComboArrayA.length !== keyComboArrayB.length) {
return false;
}
for (cI = 0; cI < keyComboArrayA.length; cI += 1) {
if (keyComboArrayA[cI].length !== keyComboArrayB[cI].length) {
return false;
}
for (sI = 0; sI < keyComboArrayA[cI].length; sI += 1) {
if (keyComboArrayA[cI][sI].length !== keyComboArrayB[cI][sI].length) {
return false;
}
for (kI = 0; kI < keyComboArrayA[cI][sI].length; kI += 1) {
if (keyComboArrayB[cI][sI].indexOf(keyComboArrayA[cI][sI][kI]) === -1) {
return false;
}
}
}
}
return true;
}
function isSatisfiedCombo(keyCombo) {
var cI, sI, stage, kI, stageOffset = 0,
index, comboMatches;
keyCombo = parseKeyCombo(keyCombo);
for (cI = 0; cI < keyCombo.length; cI += 1) {
comboMatches = true;
stageOffset = 0;
for (sI = 0; sI < keyCombo[cI].length; sI += 1) {
stage = [].concat(keyCombo[cI][sI]);
for (kI = stageOffset; kI < activeKeys.length; kI += 1) {
index = stage.indexOf(activeKeys[kI]);
if (index > -1) {
stage.splice(index, 1);
stageOffset = kI;
}
}
if (stage.length !== 0) {
comboMatches = false;
break;
}
}
if (comboMatches) {
return true;
}
}
return false;
}
function extractComboKeys(keyCombo) {
var cI, sI, kI, keys = [];
keyCombo = parseKeyCombo(keyCombo);
for (cI = 0; cI < keyCombo.length; cI += 1) {
for (sI = 0; sI < keyCombo[cI].length; sI += 1) {
keys = keys.concat(keyCombo[cI][sI]);
}
}
return keys;
}
function parseKeyCombo(keyCombo) {
var s = keyCombo,
i = 0,
op = 0,
ws = false,
nc = false,
combos = [],
combo = [],
stage = [],
key = "";
if (typeof keyCombo === "object" && typeof keyCombo.push === "function") {
return keyCombo;
}
if (typeof keyCombo !== "string") {
throw new Error('Cannot parse "keyCombo" because its type is "' + typeof keyCombo + '". It must be a "string".');
}
while (s.charAt(i) === " ") {
i += 1;
}
while (true) {
if (s.charAt(i) === " ") {
while (s.charAt(i) === " ") {
i += 1;
}
ws = true;
} else if (s.charAt(i) === ",") {
if (op || nc) {
throw new Error("Failed to parse key combo. Unexpected , at character index " + i + ".");
}
nc = true;
i += 1;
} else if (s.charAt(i) === "+") {
if (key.length) {
stage.push(key);
key = "";
}
if (op || nc) {
throw new Error("Failed to parse key combo. Unexpected + at character index " + i + ".");
}
op = true;
i += 1;
} else if (s.charAt(i) === ">") {
if (key.length) {
stage.push(key);
key = "";
}
if (stage.length) {
combo.push(stage);
stage = [];
}
if (op || nc) {
throw new Error("Failed to parse key combo. Unexpected > at character index " + i + ".");
}
op = true;
i += 1;
} else if (i < s.length - 1 && s.charAt(i) === "!" && (s.charAt(i + 1) === ">" || s.charAt(i + 1) === "," || s.charAt(i + 1) === "+")) {
key += s.charAt(i + 1);
op = false;
ws = false;
nc = false;
i += 2;
} else if (i < s.length && s.charAt(i) !== "+" && s.charAt(i) !==