UNPKG

konami-js

Version:

Multiple input support for konami code (Keyboard and Touch)

922 lines (769 loc) 27.2 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ var KonamiKeyboard = require('konami-keyboard'), KonamiTouch = require('konami-touch'); function Konami(success){ new KonamiKeyboard(success || function(){}); new KonamiTouch(success || function(){}); } Konami.prototype.constructor = Konami; module.exports = Konami; },{"konami-keyboard":2,"konami-touch":4}],2:[function(require,module,exports){ var konamiCode = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13], KeyboardCode = require('keyboard-code'); function KonamiKeyboard(success){ KeyboardCode.call(this, konamiCode, success || function(){}); } KonamiKeyboard.prototype = Object.create(KeyboardCode.prototype); KonamiKeyboard.prototype.constructor = KonamiKeyboard; module.exports = KonamiKeyboard; },{"keyboard-code":3}],3:[function(require,module,exports){ function KeyboardCode(code, success){ this.code = code || []; this.success = success || function(){}; this.codeIndex = 0; if(window.addEventListener) { window.addEventListener("keyup", this.checkCode.bind(this), false); } else { window.attachEvent("onkeyup", this.checkCode.bind(this)); } } KeyboardCode.prototype.checkCode = function checkCode(event){ if(event.keyCode === this.code[this.codeIndex++]){ if(this.codeIndex === this.code.length){ this.success(); this.codeIndex = 0; } }else{ this.codeIndex = 0; } }; module.exports = KeyboardCode; },{}],4:[function(require,module,exports){ var konamiCode = ['up', 'up', 'down', 'down', 'left', 'right', 'left', 'right', 'tap', 'tap', 'tap'], GestureDetector = require('gestures'), radiansToDegrees = require('math-js/angles/radiansToDegrees'), linearRegresion = require('./linearRegresion'); function getGestureVector(moves){ var line = linearRegresion(moves), direction = 0, magitude = 0; if(line.length>2){ var startPoint = line[0], endPoint = line[line.length - 1]; direction = radiansToDegrees(Math.atan2(-(startPoint.x - endPoint.x), startPoint.y - endPoint.y)); magitude = Math.sqrt(Math.pow(Math.abs(startPoint.x - endPoint.x), 2) + Math.pow(Math.abs(startPoint.y - endPoint.y), 2)); } return { direction: direction, magitude: magitude }; } function KonamiTouch(success){ this.code = konamiCode; this.success = success || function(){}; this.codeIndex = 0; var detector = new GestureDetector(); detector.gestures.push(function(moves){ var vector = getGestureVector(moves); if(vector.magitude < 5){ return 'tap'; } if(vector.magitude > 5 && vector.magitude < 20){ return; } if(vector.direction > -45 && vector.direction < 45){ return 'up'; } if(vector.direction < -135 || vector.direction > 135){ return 'down'; } if(vector.direction < -45 && vector.direction > -135){ return 'left'; } if(vector.direction > 45 && vector.direction < 135){ return 'right'; } }); detector.on('gesture', this.checkCode.bind(this)); } KonamiTouch.prototype.checkCode = function checkCode(event){ if(event.name === this.code[this.codeIndex++]){ if(this.codeIndex === this.code.length){ this.success(); this.codeIndex = 0; } }else{ this.codeIndex = 0; } }; module.exports = KonamiTouch; },{"./linearRegresion":5,"gestures":6,"math-js/angles/radiansToDegrees":8}],5:[function(require,module,exports){ module.exports = function linearRegresion(points) { var sumX = 0, sumY = 0, sumXbyY = 0, sumXbyX = 0, x = 0, y = 0, numberOfPoints = points.length, results = [], m, b; if (numberOfPoints === 0) { return []; } for (var i = 0; i < numberOfPoints; i++) { x = points[i].x; y = points[i].y; sumX += x; sumY += y; sumXbyX += x*x; sumXbyY += x*y; } m = (numberOfPoints * sumXbyY - sumX * sumY) / (numberOfPoints * sumXbyX - sumX * sumX); b = (sumY / numberOfPoints) - (m * sumX) / numberOfPoints; for (var i = 0; i < numberOfPoints; i++) { results.push({ x: points[i].x, y: points[i].x * m + b }); } return results; } },{}],6:[function(require,module,exports){ var interact = require('interact-js'), EventEmitter = require('events').EventEmitter; function GestureDetector(target){ this.target = target || document; interact.on('start', this.target, this.__boundStart = this._start.bind(this)); interact.on('drag', this.target, this.__boundStart = this._drag.bind(this)); interact.on('end', this.target, this.__boundEnd = this._end.bind(this)); this.gestures = []; } GestureDetector.prototype = Object.create(EventEmitter.prototype); GestureDetector.prototype.constructor = GestureDetector; GestureDetector.prototype._start = function(interaction) { interaction.points = []; }; GestureDetector.prototype._drag = function(interaction) { interaction.points.push({ x: interaction.pageX, y: interaction.pageY }); }; GestureDetector.prototype._end = function(interaction) { var detector = this; this.gestures.forEach(function(gesture){ var gestureName = gesture.call(this, interaction.points); if(gestureName){ detector.emit('gesture', { name: gestureName, points: interaction.points }); } }); }; GestureDetector.destroy = function(){ interact.off('start', this.target, this.__boundStart); interact.on('end', this.target, this.__boundEnd); }; module.exports = GestureDetector; },{"events":11,"interact-js":7}],7:[function(require,module,exports){ var interactions = [], minMoveDistance = 5, interact, maximumMovesToPersist = 1000, // Should be plenty.. propertiesToCopy = 'target,pageX,pageY,clientX,clientY,offsetX,offsetY,screenX,screenY,shiftKey,x,y'.split(','); // Stuff that will be on every interaction. function Interact(){ this._elements = []; } Interact.prototype.on = function(eventName, target, callback){ if(!target){ return; } target._interactEvents = target._interactEvents || {}; target._interactEvents[eventName] = target._interactEvents[eventName] || [] target._interactEvents[eventName].push({ callback: callback, interact: this }); return this; }; Interact.prototype.emit = function(eventName, target, event, interaction){ if(!target){ return; } var interact = this, currentTarget = target; interaction.originalEvent = event; interaction.preventDefault = function(){ event.preventDefault(); } interaction.stopPropagation = function(){ event.stopPropagation(); } while(currentTarget){ currentTarget._interactEvents && currentTarget._interactEvents[eventName] && currentTarget._interactEvents[eventName].forEach(function(listenerInfo){ if(listenerInfo.interact === interact){ listenerInfo.callback.call(interaction, interaction); } }); currentTarget = currentTarget.parentNode; } return this; }; Interact.prototype.off = Interact.prototype.removeListener = function(eventName, target, callback){ if(!target || !target._interactEvents || !target._interactEvents[eventName]){ return; } var interactListeners = target._interactEvents[eventName], listenerInfo; for(var i = 0; i < interactListeners.length; i++) { listenerInfo = interactListeners[i]; if(listenerInfo.interact === interact && listenerInfo.callback === callback){ interactListeners.splice(i,1); i--; } } return this; }; interact = new Interact(); // For some reason touch browsers never change the event target during a touch. // This is, lets face it, fucking stupid. function getActualTarget() { var scrollX = window.scrollX, scrollY = window.scrollY; // IE is stupid and doesn't support scrollX/Y if(scrollX === undefined){ scrollX = document.body.scrollLeft; scrollY = document.body.scrollTop; } return document.elementFromPoint(this.pageX - window.scrollX, this.pageY - window.scrollY); } function getMoveDistance(x1,y1,x2,y2){ var adj = Math.abs(x1 - x2), opp = Math.abs(y1 - y2); return Math.sqrt(Math.pow(adj,2) + Math.pow(opp,2)); } function destroyInteraction(interaction){ for(var i = 0; i < interactions.length; i++){ if(interactions[i].identifier === interaction.identifier){ interactions.splice(i,1); } } } function getInteraction(identifier){ for(var i = 0; i < interactions.length; i++){ if(interactions[i].identifier === identifier){ return interactions[i]; } } } function setInheritedData(interaction, data){ for(var i = 0; i < propertiesToCopy.length; i++) { interaction[propertiesToCopy[i]] = data[propertiesToCopy[i]] } } function Interaction(event, interactionInfo){ // If there is no event (eg: desktop) just make the identifier undefined if(!event){ event = {}; } // If there is no extra info about the interaction (eg: desktop) just use the event itself if(!interactionInfo){ interactionInfo = event; } // If there is another interaction with the same ID, something went wrong. // KILL IT WITH FIRE! var oldInteraction = getInteraction(interactionInfo.identifier); oldInteraction && oldInteraction.destroy(); this.identifier = interactionInfo.identifier; this.moves = []; interactions.push(this); } Interaction.prototype = { constructor: Interaction, getActualTarget: getActualTarget, destroy: function(){ interact.on('destroy', this.target, this, this); destroyInteraction(this); }, start: function(event, interactionInfo){ // If there is no extra info about the interaction (eg: desktop) just use the event itself if(!interactionInfo){ interactionInfo = event; } var lastStart = { time: new Date() }; setInheritedData(lastStart, interactionInfo); this.lastStart = lastStart; setInheritedData(this, interactionInfo); interact.emit('start', event.target, event, this); return this; }, move: function(event, interactionInfo){ // If there is no extra info about the interaction (eg: desktop) just use the event itself if(!interactionInfo){ interactionInfo = event; } var currentTouch = { time: new Date() }; setInheritedData(currentTouch, interactionInfo); // Update the interaction setInheritedData(this, interactionInfo); this.moves.push(currentTouch); // Memory saver, culls any moves that are over the maximum to keep. this.moves = this.moves.slice(-maximumMovesToPersist); var lastMove = this.moves[this.moves.length-2]; lastMove && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastMove.pageY, currentTouch.pageX - lastMove.pageX) * 180 / Math.PI); this.angle = currentTouch.angle || 0; interact.emit('move', event.target, event, this); return this; }, drag: function(event, interactionInfo){ // If there is no extra info about the interaction (eg: desktop) just use the event itself if(!interactionInfo){ interactionInfo = event; } var currentTouch = { time: new Date(), isDrag: true }; setInheritedData(currentTouch, interactionInfo); // Update the interaction setInheritedData(this, interactionInfo); if(!this.moves){ this.moves = []; } this.moves.push(currentTouch); // Memory saver, culls any moves that are over the maximum to keep. this.moves = this.moves.slice(-maximumMovesToPersist); if(!this.dragStarted && getMoveDistance(this.lastStart.pageX, this.lastStart.pageY, currentTouch.pageX, currentTouch.pageY) > minMoveDistance){ this.dragStarted = true; } var lastDrag = this.moves[this.moves.length-2] || this.lastStart; lastDrag && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastDrag.pageY, currentTouch.pageX - lastDrag.pageX) * 180 / Math.PI); this.angle = currentTouch.angle || 0; if(this.dragStarted){ interact.emit('drag', event.target, event, this); } return this; }, end: function(event, interactionInfo){ if(!interactionInfo){ interactionInfo = event; } // Update the interaction setInheritedData(this, interactionInfo); interact.emit('end', event.target, event, this); return this; }, cancel: function(event, interactionInfo){ if(!interactionInfo){ interactionInfo = event; } // Update the interaction setInheritedData(this, interactionInfo); interact.emit('cancel', event.target, event, this); return this; }, getMoveDistance: function(){ if(this.moves.length > 1){ var current = this.moves[this.moves.length-1], previous = this.moves[this.moves.length-2]; return getMoveDistance(current.pageX, current.pageY, previous.pageX, previous.pageY); } }, getMoveDelta: function(){ if(this.moves.length > 1){ var current = this.moves[this.moves.length-1], previous = this.moves[this.moves.length-2]; return { x: current.pageX - previous.pageX, y: current.pageY - previous.pageY }; } }, getSpeed: function(){ if(this.moves.length > 1){ var current = this.moves[this.moves.length-1], previous = this.moves[this.moves.length-2]; return this.getMoveDistance() / (current.time - previous.time); } return 0; }, getCurrentAngle: function(blend){ var currentPosition, lastAngle, i = this.moves.length-1, angle, firstAngle, angles = [], blendSteps = 20/(this.getSpeed()*2+1), stepsUsed = 0; if(this.moves && this.moves.length){ currentPosition = this.moves[i]; angle = firstAngle = currentPosition.angle; if(blend && this.moves.length > 1){ while(--i > 0 && this.moves.length - i < blendSteps){ lastAngle = this.moves[i].angle; if(Math.abs(lastAngle - firstAngle) > 180){ angle -= lastAngle }else{ angle += lastAngle } stepsUsed++; } angle = angle/stepsUsed; } } return angle; }, getAllInteractions: function(){ return interactions.slice(); } }; function start(event){ var touch; for(var i = 0; i < event.changedTouches.length; i++){ touch = event.changedTouches[i]; new Interaction(event, event.changedTouches[i]).start(event, touch); } } function drag(event){ var touch; for(var i = 0; i < event.changedTouches.length; i++){ touch = event.changedTouches[i]; getInteraction(touch.identifier).drag(event, touch); } } function end(event){ var touch; for(var i = 0; i < event.changedTouches.length; i++){ touch = event.changedTouches[i]; getInteraction(touch.identifier).end(event, touch).destroy(); } } function cancel(event){ var touch; for(var i = 0; i < event.changedTouches.length; i++){ touch = event.changedTouches[i]; getInteraction(touch.identifier).cancel(event, touch).destroy(); } } addEvent(document, 'touchstart', start); addEvent(document, 'touchmove', drag); addEvent(document, 'touchend', end); addEvent(document, 'touchcancel', cancel); var mouseIsDown = false; addEvent(document, 'mousedown', function(event){ mouseIsDown = true; if(!interactions.length){ new Interaction(event); } getInteraction().start(event); }); addEvent(document, 'mousemove', function(event){ if(!interactions.length){ new Interaction(event); } var interaction = getInteraction(); if(!interaction){ return; } if(mouseIsDown){ interaction.drag(event); }else{ interaction.move(event); } }); addEvent(document, 'mouseup', function(event){ mouseIsDown = false; var interaction = getInteraction(); if(!interaction){ return; } interaction.end(event, null); }); function addEvent(element, type, callback) { if(element.addEventListener){ element.addEventListener(type, callback); } else if(document.attachEvent){ element.attachEvent("on"+ type, callback); } } module.exports = interact; },{}],8:[function(require,module,exports){ var constants = require('../constants'); module.exports = function(radians) { return (radians / constants.radiansInACircle) * constants.degreesInACircle; }; },{"../constants":9}],9:[function(require,module,exports){ var pi = Math.PI; module.exports = { pi: pi, degreesInACircle: 360, radiansInACircle: 2 * pi }; },{}],10:[function(require,module,exports){ var Konami = require('../'); new Konami(function(){ alert('KONAMI!'); }); },{"../":1}],11:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } else { throw TypeError('Uncaught, unspecified "error" event.'); } return false; } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; handler.apply(this, args); } } else if (isObject(handler)) { len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { var m; if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); console.trace(); } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.listenerCount = function(emitter, type) { var ret; if (!emitter._events || !emitter._events[type]) ret = 0; else if (isFunction(emitter._events[type])) ret = 1; else ret = emitter._events[type].length; return ret; }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}]},{},[10])