Skip to content

Instantly share code, notes, and snippets.

@crossai-2033
Created September 16, 2013 02:24
Show Gist options
  • Save crossai-2033/6576073 to your computer and use it in GitHub Desktop.
Save crossai-2033/6576073 to your computer and use it in GitHub Desktop.
javascript:Konami
(function() {
var EventTargetH = QW.EventTargetH,
on = EventTargetH.on,
mix = QW.ObjectH.mix,
isFunction = QW.ObjectH.isFunction,
filter = QW.Selector.filter,
keys = QW.ObjectH.keys,
forEach = QW.ArrayH.forEach;
// 事件队列
function fnQueue() {
var queue = [],
dup = false;
function getCallMethod(type) {
return function() {
var re, fn;
dup = this.slice().reverse();
while (fn = dup.pop()) {
re = fn[type].apply(fn, arguments);
}
dup = false;
return re;
};
}
mix(queue, {
call: getCallMethod('call'),
apply: getCallMethod('apply'),
clear: function(func) {
if (!func) {
this.length = 0;
} else {
var size = this.length,
popsize = size - dup.length;
for (var i = this.length - 1; i >= 0; i--) {
if (this[i] === func) {
this.splice(i, 1);
if (dup && i >= popsize) {
dup.splice(size - i - 1, 1);
}
}
}
if (i < 0) return false;
}
return true;
}
});
return queue;
}
var Konami = function() {
var _keyMap = {
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", 20: "capslock",
27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", 37: "left", 38: "up", 39: "right",
40: "down", 45: "insert", 46: "del", 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", 104: "8",
105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111: "/", 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6",
118: "f7", 119: "f8", 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
};
var _shiftMap = {
"`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", "8": "*", "9": "(", "0": ")",
"-": "_", "=": "+", ";": ":", "'": '"', ",": "<", ".": ">", "/": "?", "\\": "|"
};
function KonamiHelper(options) {
var self = this;
options = options || {};
this.target = options.target || document; // 按键载体
this.keyHandlers = {};
this.globalKeyHandlers = {};
this.rules = [];
this.sequence = {};
this.sequenceNums = [];
this.history = [];
this.trace = options.trace;
this.traceStack = options.traceStack || [];
this.forTextarea = options.forTextarea || false;
this._handler = function(e) {
if (self.forTextarea && (!/textarea|input/i.test(e.target.nodeName) && e.target.type !== 'text')) {
return;
}
if (!self.forTextarea && this !== e.target && (/textarea|select/i.test(e.target.nodeName) || e.target.type === 'text')) {
return;
}
var result, lock = self.lock || !self.check(this, e),
keyHandler = self.keyHandlers[e.type],
globalKeyHandler = self.globalKeyHandlers[e.type];
if (keyHandler) {
var keyStore = parseKey(e);
var action, store;
for (var n in keyStore) {
if (keyStore[n]) {
break;
}
}
// 如果在队列中,并没有被锁定
if (self.sequenceNums.length && !lock) {
var his = self.history;
his.push(n);
if (his.length > 10) {
his.shift();
}
if (his.length > 1) {
for (var len = self.sequenceNums.length - 1; len >= 0; len--) {
action = keyHandler[his.slice(0 - self.sequenceNums[len]).join("->")];
if (action) {
if (self.trace) {
self._trace(len);
}
result = action.apply(this, arguments);
his.length = 0;
if (!result) {
e.preventDefault(); // 需要兼容
}
return result;
}
}
}
}
if (store) {
if (lock) {
return false;
}
if (self.trace) {
self._trace(n);
}
result = action.apply(this, arguments);
if (!result) {
e.preventDefault();
}
}
}
if (globalKeyHandler) {
if (lock) {
return false;
}
result = globalKeyHandler.apply(this, arguments);
if (!result) {
e.preventDefault();
}
}
return result;
};
}
KonamiHelper.prototype = {
addHandler: function(action, command, handler) {
var self = this;
var handlers = this.keyHandlers[action];
if (!handler) {
handler = command;
command = '';
}
function parseCommand(command) {
if (command) {
var commandArr = command.split('->');
if (commandArr.length > 1) {
self.sequence[commandArr.length] = 1;
var tmp = [];
for (var i in self.sequence) {
tmp.push(parseInt(i, 10));
}
self.sequenceNums = tmp.sort(function(a, b) {
return a - b;
});
}
var key = command.toLowerCase();
if (!handlers[key]) {
handlers[key] = fnQueue();
}
handlers[key].push(handler);
} else {
var globalHandler = self.globalKeyHandlers[action];
if (!globalHandler) {
globalHandler = self.globalKeyHandlers[action] = fnQueue();
}
globalHandler.push(handler);
}
}
if (!handlers) {
handlers = this.keyHandlers[action] = {};
EventTargetH.on(this.target, action, this._handler);
}
if (Array.isArray(command)) {
command.forEach(function(c) {
parseCommand(c);
});
} else {
parseCommand(command);
}
return this;
},
_trace: function(h) {
this.traceStack.unshift("[" + h + "]");
if (this.traceStack.length > this.trace) {
this.traceStack.pop();
}
},
reset: function() {
for (var k in this.keyHandlers) {
EventTargetH.un(k, this._handler);
}
this.keyHandlers = {};
this.rules = [];
this.history = [];
delete this._handler;
this.lock = false;
},
addRule: function(h) {
this.rules.push(h);
return this;
},
enable: function() {
this.lock = false;
},
disable: function() {
this.lock = true;
},
check: function(target, e) {
var k = true,
rules = this.rules;
for (var j = 0, h = rules.length; j < h; j++) {
if (!rules[j].call(target, e)) {
k = false;
break
}
}
return k;
}
};
(['down', 'up', 'press']).forEach(function(action) {
this[action] = function(command, handler) {
this.addHandler("key" + action, command, handler);
return this;
};
}, KonamiHelper.prototype);
function parseKey(ev) {
var key = ev.type !== 'keypress' && _keyMap[ev.which],
keyAscii = String.fromCharCode(ev.which).toLowerCase(),
keyJoin = '',
keyStore = {};
if (ev.altKey && key !== 'alt') {
keyJoin += "alt+";
}
if (ev.ctrlKey && key !== 'ctrl') {
keyJoin += "ctrl+";
}
if (ev.metaKey && !ev.ctrlKey && key !== 'meta') {
keyJoin += "meta+";
}
if (ev.shiftKey && key !== 'shift') {
keyJoin += "shift+";
}
if (key) {
keyStore[keyJoin + key] = true;
} else {
var join = keyJoin + keyAscii;
if (join) {
keyStore[join] = true;
}
join = _shiftMap[keyAscii];
if (join) {
keyStore[keyJoin + join] = true;
if (keyJoin === 'shift+') {
join = _shiftMap[keyAscii];
if (join) {
keyStore[join] = true;
}
}
}
}
return keyStore;
}
function exports(options) {
return new KonamiHelper(options);
}
exports.KEYS_CODE = _keyMap;
return exports;
}();
QW.provide('Konami', Konami);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment