Created
September 16, 2013 02:24
-
-
Save crossai-2033/6576073 to your computer and use it in GitHub Desktop.
javascript:Konami
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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