scratchcard
Version:
Mimic a scratchcard with HTML5
1,890 lines (1,608 loc) • 183 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Scratchcard = f()}})(function(){var define,module,exports;return (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);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.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){
/* jshint browserify: true */
'use strict';
var defaults = require('lodash.defaults');
module.exports = exports = Painter;
var defaultOptions = {
color: 'silver',
thickness: 50
};
function Painter(options) {
this.options = defaults({}, options, defaultOptions);
}
Painter.prototype.reset = function reset(ctx, width, height) {
ctx.fillStyle = this.options.color;
ctx.globalCompositeOperation = 'source-over';
ctx.fillRect(0, 0, width, height);
};
Painter.prototype.drawPoint = function drawPoint(ctx, point) {
ctx.fillStyle = 'rgba(0,0,0,1)';
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.arc(point.x, point.y, this.options.thickness / 2, 0, 2 * Math.PI);
ctx.fill();
};
Painter.prototype.drawLine = function drawLine(ctx, start, end) {
ctx.lineWidth = this.options.thickness;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = 'rgba(0,0,0,1)';
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
};
Painter.prototype.complete = function complete(ctx, width, height) {
ctx.fillStyle = 'rgba(0,0,0,1)';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillRect(0, 0, width, height);
};
Painter.prototype.drawLayer = function drawLayer(ctx, width, height, index) {};
},{"lodash.defaults":13}],2:[function(require,module,exports){
/* jshint browserify: true */
'use strict';
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var defaults = require('lodash.defaults');
var Painter = Scratchcard.Painter = require('./painter');
module.exports = exports = Scratchcard;
var defaultOptions = {
threshold: 255,
realtime: false,
layers: 0,
painter: {},
animationFrame: null
};
function Scratchcard(element, options) {
if (isIeLessThan9()) {
throw new Error('Internet Explorer not supported prior to version 9');
}
EventEmitter.call(this);
var self = this;
var canvas = document.createElement('canvas');
if (!canvas.getContext) {
throw new Error('HTML5 canvas not supported');
}
var ctx = canvas.getContext('2d');
options = (options instanceof Painter) ?
defaults({painter: options}, defaultOptions) :
defaults({}, options, defaultOptions);
var requestAnimationFrame = window.requestAnimationFrame;
var animationFrame = options.animationFrame;
if (animationFrame && animationFrame.request) {
requestAnimationFrame = animationFrame.request.bind(animationFrame);
}
if (!requestAnimationFrame) {
throw new Error('requestAnimationFrame not supported');
}
var size = getSize(element);
canvas.style.position = 'absolute';
canvas.width = size.width;
canvas.height = size.height;
canvas.style.zIndex = options.layers + 1;
// Disable the blue overlay for some browsers
canvas.style['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
var wrapper = document.createElement('div');
wrapper.className = 'scratchcard';
wrapper.style.position = 'relative';
wrapper.style.display = 'inline';
wrapper.style.verticalAlign = 'top';
wrapper.appendChild(canvas);
var layers = [];
for (var index = 0; index < options.layers; index++) {
var layer = document.createElement('canvas');
layer.style.position = 'absolute';
layer.width = size.width;
layer.height = size.height;
layer.style.zIndex = index + 1;
layer.style.pointerEvents = 'none';
layers.push({
canvas: layer,
ctx: layer.getContext('2d')
});
wrapper.appendChild(layer);
}
var painter = null;
var previousFingers = {};
var previousFingersCount = 0;
var currentFingers = {};
var currentFingersCount = 0;
var updated = false;
var progress = null;
var completed = false;
var animated = false;
this.getElement = getElement;
this.setElement = setElement;
this.getPainter = getPainter;
this.setPainter = setPainter;
this.getWrapper = getWrapper;
this.getCanvas = getCanvas;
this.reset = reset;
this.complete = complete;
this.getProgress = getProgress;
setPainter(options.painter);
if (element) {
element.parentNode.insertBefore(wrapper, element);
element.style.visibility = 'visible';
canvas.addEventListener('mousedown', onMouseDown, true);
canvas.addEventListener('touchstart', onTouchStart, true);
animate();
}
function getElement() {
return element;
}
function setElement(newElement) {
if (newElement) {
element = newElement;
synchronize();
canvas.addEventListener('mousedown', onMouseDown, true);
canvas.addEventListener('touchstart', onTouchStart, true);
if (!animated) {
animate();
}
} else if (element) {
element = null;
wrapper.parentNode.removeChild(wrapper);
canvas.removeEventListener('mousedown', onMouseDown, true);
canvas.removeEventListener('touchstart', onTouchStart, true);
}
}
function getPainter() {
return painter;
}
function setPainter(newPainter) {
painter = (newPainter instanceof Painter) ? newPainter : new Painter(newPainter);
redraw();
}
function getWrapper() {
return wrapper;
}
function getCanvas() {
return canvas;
}
function reset() {
painter.reset(ctx, canvas.width, canvas.height);
updated = true;
completed = false;
canvas.style.pointerEvents = null;
checkProgress();
}
function drawPoint(point) {
painter.drawPoint(ctx, point);
updated = true;
}
function drawLine(start, end) {
painter.drawLine(ctx, start, end);
updated = true;
}
function complete() {
painter.complete(ctx, canvas.width, canvas.height);
updated = true;
completed = true;
canvas.style.pointerEvents = 'none';
checkProgress();
}
function redraw() {
if (completed) {
painter.complete(ctx, canvas.width, canvas.height);
} else {
painter.reset(ctx, canvas.width, canvas.height);
}
updated = true;
for (var index = 0, count = layers.length; index < count; index++) {
var layer = layers[index];
painter.drawLayer(layer.ctx, layer.canvas.width, layer.canvas.height, index);
}
checkProgress();
}
function getProgress() {
var width = canvas.width;
var height = canvas.height;
var pixels = width * height;
if (pixels === 0) {
return 0;
}
var holes = 0;
var data = ctx.getImageData(0, 0, width, height).data;
for (var index = 3, count = data.length; index < count; index += 4) {
if (data[index] >= options.threshold) {
holes++;
}
}
return (pixels - holes) / pixels;
}
function checkProgress() {
if (!updated) {
return;
}
var lastProgress = progress;
progress = getProgress();
updated = false;
if (progress !== lastProgress) {
self.emit('progress', progress);
if ((progress === 1) && (!completed)) {
complete();
}
}
}
function synchronize() {
var parent = element.parentNode;
if (!parent) {
setElement(null);
return;
}
if (wrapper.nextSibling !== element) {
parent.insertBefore(wrapper, element);
}
var size = getSize(element);
if ((canvas.width === size.width) && (canvas.height === size.height)) {
return;
}
canvas.width = size.width;
canvas.height = size.height;
for (var index = 0, count = layers.length; index < count; index++) {
var layer = layers[index].canvas;
layer.width = size.width;
layer.height = size.height;
}
redraw();
}
function animate() {
if (!element) {
animated = false;
return;
}
requestAnimationFrame(animate);
animated = true;
synchronize();
for (var identifier in currentFingers) {
var previousFinger = previousFingers[identifier];
var currentFinger = currentFingers[identifier];
if (!previousFinger) {
drawPoint(currentFinger);
} else if ((currentFinger.x !== previousFinger.x) || (currentFinger.y !== previousFinger.y)) {
drawLine(previousFinger, currentFinger);
}
}
if (options.realtime || ((currentFingersCount === 0) && (previousFingersCount > 0))) {
checkProgress();
}
previousFingers = currentFingers;
previousFingersCount = currentFingersCount;
}
function onMouseDown(event) {
if (event.button !== 0) {
return;
}
event.preventDefault();
// console.log('mousedown');
var boundingRect = canvas.getBoundingClientRect();
currentFingers = {
mouse: {
x: event.clientX - boundingRect.left,
y: event.clientY - boundingRect.top
}
};
currentFingersCount = 1;
window.addEventListener('mousemove', onMouseMove, true);
window.addEventListener('mouseup', onMouseUp, true);
canvas.removeEventListener('mousedown', onMouseDown, true);
canvas.removeEventListener('touchstart', onTouchStart, true);
}
function onMouseMove(event) {
if (event.button !== 0) {
return;
}
event.preventDefault();
// console.log('mousemove');
var boundingRect = canvas.getBoundingClientRect();
currentFingers = {
mouse: {
x: event.clientX - boundingRect.left,
y: event.clientY - boundingRect.top
}
};
currentFingersCount = 1;
}
function onMouseUp(event) {
if (event.button !== 0) {
return;
}
event.preventDefault();
// console.log('mouseup');
currentFingers = {};
currentFingersCount = 0;
window.removeEventListener('mousemove', onMouseMove, true);
window.removeEventListener('mouseup', onMouseUp, true);
if (element) {
canvas.addEventListener('mousedown', onMouseDown, true);
canvas.addEventListener('touchstart', onTouchStart, true);
}
}
function onTouchStart(event) {
event.preventDefault();
// console.log('touchstart');
currentFingers = {};
currentFingersCount = event.touches.length;
var boundingRect = canvas.getBoundingClientRect();
for (var index = 0; index < currentFingersCount; index++) {
var touch = event.touches[index];
currentFingers[touch.identifier] = {
x: touch.clientX - boundingRect.left,
y: touch.clientY - boundingRect.top
};
}
window.addEventListener('touchstart', onTouch, true);
window.addEventListener('touchmove', onTouch, true);
window.addEventListener('touchend', onTouch, true);
canvas.removeEventListener('mousedown', onMouseDown, true);
canvas.removeEventListener('touchstart', onTouchStart, true);
}
function onTouch(event) {
event.preventDefault();
// console.log(event.type);
currentFingers = {};
currentFingersCount = event.touches.length;
if (currentFingersCount > 0) {
var boundingRect = canvas.getBoundingClientRect();
for (var index = 0; index < currentFingersCount; index++) {
var touch = event.touches[index];
currentFingers[touch.identifier] = {
x: touch.clientX - boundingRect.left,
y: touch.clientY - boundingRect.top
};
}
} else {
window.removeEventListener('touchstart', onTouch, true);
window.removeEventListener('touchmove', onTouch, true);
window.removeEventListener('touchend', onTouch, true);
if (element) {
canvas.addEventListener('mousedown', onMouseDown, true);
canvas.addEventListener('touchstart', onTouchStart, true);
}
}
}
}
util.inherits(Scratchcard, EventEmitter);
function isIeLessThan9() {
var div = document.createElement('div');
div.innerHTML = '<!--[if lt IE 9]><i></i><![endif]-->';
return (div.getElementsByTagName('i').length === 1);
}
var zero = {width: 0, height: 0};
function getSize(element) {
if (!element) {
return zero;
}
var rects = element.getClientRects();
if (rects.length === 0) {
return zero;
}
var firstRect = rects[0];
return {
width: Math.ceil(firstRect.right) - Math.floor(firstRect.left),
height: Math.ceil(firstRect.bottom) - Math.floor(firstRect.top)
};
}
},{"./painter":1,"events":11,"lodash.defaults":13,"util":16}],3:[function(require,module,exports){
/* jshint browserify: true */
'use strict';
var util = require('util');
var defaults = require('lodash.defaults');
var AnimationFrame = require('animation-frame');
var Scratchcard = require('./scratchcard');
var Painter = require('./painter');
StandaloneScratchard.Painter = Painter;
module.exports = exports = StandaloneScratchard;
function StandaloneScratchard(element, options) {
options = (options instanceof Painter) ?
{painter: options} :
defaults({}, options);
if (!(options.animationFrame instanceof AnimationFrame)) {
options.animationFrame = new AnimationFrame(options.animationFrame);
}
Scratchcard.call(this, element, options);
}
util.inherits(StandaloneScratchard, Scratchcard);
},{"./painter":1,"./scratchcard":2,"animation-frame":4,"lodash.defaults":13,"util":16}],4:[function(require,module,exports){
/**
* An even better animation frame.
*
* @copyright Oleg Slobodskoi 2016
* @website https://github.com/kof/animationFrame
* @license MIT
*/
module.exports = require('./lib/animation-frame')
},{"./lib/animation-frame":5}],5:[function(require,module,exports){
'use strict'
var nativeImpl = require('./native')
var now = require('./now')
var performance = require('./performance')
var root = require('./root')
// Weird native implementation doesn't work if context is defined.
var nativeRequest = nativeImpl.request
var nativeCancel = nativeImpl.cancel
/**
* Animation frame constructor.
*
* Options:
* - `useNative` use the native animation frame if possible, defaults to true
* - `frameRate` pass a custom frame rate
*
* @param {Object|Number} options
*/
function AnimationFrame(options) {
if (!(this instanceof AnimationFrame)) return new AnimationFrame(options)
options || (options = {})
// Its a frame rate.
if (typeof options == 'number') options = {frameRate: options}
options.useNative != null || (options.useNative = true)
this.options = options
this.frameRate = options.frameRate || AnimationFrame.FRAME_RATE
this._frameLength = 1000 / this.frameRate
this._isCustomFrameRate = this.frameRate !== AnimationFrame.FRAME_RATE
this._timeoutId = null
this._callbacks = {}
this._lastTickTime = 0
this._tickCounter = 0
}
module.exports = AnimationFrame
/**
* Default frame rate used for shim implementation. Native implementation
* will use the screen frame rate, but js have no way to detect it.
*
* If you know your target device, define it manually.
*
* @type {Number}
* @api public
*/
AnimationFrame.FRAME_RATE = 60
/**
* Replace the globally defined implementation or define it globally.
*
* @param {Object|Number} [options]
* @api public
*/
AnimationFrame.shim = function(options) {
var animationFrame = new AnimationFrame(options)
root.requestAnimationFrame = function(callback) {
return animationFrame.request(callback)
}
root.cancelAnimationFrame = function(id) {
return animationFrame.cancel(id)
}
return animationFrame
}
/**
* Request animation frame.
* We will use the native RAF as soon as we know it does works.
*
* @param {Function} callback
* @return {Number} timeout id or requested animation frame id
* @api public
*/
AnimationFrame.prototype.request = function(callback) {
var self = this
// Alawys inc counter to ensure it never has a conflict with the native counter.
// After the feature test phase we don't know exactly which implementation has been used.
// Therefore on #cancel we do it for both.
++this._tickCounter
if (nativeImpl.supported && this.options.useNative && !this._isCustomFrameRate) {
return nativeRequest(callback)
}
if (!callback) throw new TypeError('Not enough arguments')
if (this._timeoutId == null) {
// Much faster than Math.max
// http://jsperf.com/math-max-vs-comparison/3
// http://jsperf.com/date-now-vs-date-gettime/11
var delay = this._frameLength + this._lastTickTime - now()
if (delay < 0) delay = 0
this._timeoutId = setTimeout(function() {
self._lastTickTime = now()
self._timeoutId = null
++self._tickCounter
var callbacks = self._callbacks
self._callbacks = {}
for (var id in callbacks) {
if (callbacks[id]) {
if (nativeImpl.supported && self.options.useNative) {
nativeRequest(callbacks[id])
} else {
callbacks[id](performance.now())
}
}
}
}, delay)
}
this._callbacks[this._tickCounter] = callback
return this._tickCounter
}
/**
* Cancel animation frame.
*
* @param {Number} timeout id or requested animation frame id
*
* @api public
*/
AnimationFrame.prototype.cancel = function(id) {
if (nativeImpl.supported && this.options.useNative) nativeCancel(id)
delete this._callbacks[id]
}
},{"./native":6,"./now":7,"./performance":9,"./root":10}],6:[function(require,module,exports){
'use strict'
var root = require('./root')
// Test if we are within a foreign domain. Use raf from the top if possible.
try {
// Accessing .name will throw SecurityError within a foreign domain.
root.top.name
root = root.top
} catch(e) {}
exports.request = root.requestAnimationFrame
exports.cancel = root.cancelAnimationFrame || root.cancelRequestAnimationFrame
exports.supported = false
var vendors = ['Webkit', 'Moz', 'ms', 'O']
// Grab the native implementation.
for (var i = 0; i < vendors.length && !exports.request; i++) {
exports.request = root[vendors[i] + 'RequestAnimationFrame']
exports.cancel = root[vendors[i] + 'CancelAnimationFrame'] ||
root[vendors[i] + 'CancelRequestAnimationFrame']
}
// Test if native implementation works.
// There are some issues on ios6
// http://shitwebkitdoes.tumblr.com/post/47186945856/native-requestanimationframe-broken-on-ios-6
// https://gist.github.com/KrofDrakula/5318048
if (exports.request) {
exports.request.call(null, function() {
exports.supported = true
})
}
},{"./root":10}],7:[function(require,module,exports){
'use strict'
/**
* Crossplatform Date.now()
*
* @return {Number} time in ms
* @api private
*/
module.exports = Date.now || function() {
return (new Date).getTime()
}
},{}],8:[function(require,module,exports){
'use strict'
var now = require('./now')
/**
* Replacement for PerformanceTiming.navigationStart for the case when
* performance.now is not implemented.
*
* https://developer.mozilla.org/en-US/docs/Web/API/PerformanceTiming.navigationStart
*
* @type {Number}
* @api private
*/
exports.navigationStart = now()
},{"./now":7}],9:[function(require,module,exports){
'use strict'
var now = require('./now')
var PerformanceTiming = require('./performance-timing')
var root = require('./root')
/**
* Crossplatform performance.now()
*
* https://developer.mozilla.org/en-US/docs/Web/API/Performance.now()
*
* @return {Number} relative time in ms
* @api public
*/
exports.now = function () {
if (root.performance && root.performance.now) return root.performance.now()
return now() - PerformanceTiming.navigationStart
}
},{"./now":7,"./performance-timing":8,"./root":10}],10:[function(require,module,exports){
var root
// Browser window
if (typeof window !== 'undefined') {
root = window
// Web Worker
} else if (typeof self !== 'undefined') {
root = self
// Other environments
} else {
root = this
}
module.exports = root
},{}],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 {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
}
}
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:
args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
} else if (isObject(handler)) {
args = Array.prototype.slice.call(arguments, 1);
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) {
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);
if (typeof console.trace === 'function') {
// not supported in IE 10
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 if (listeners) {
// 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.prototype.listenerCount = function(type) {
if (this._events) {
var evlistener = this._events[type];
if (isFunction(evlistener))
return 1;
else if (evlistener)
return evlistener.length;
}
return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
return emitter.listenerCount(type);
};
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;
}
},{}],12:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],13:[function(require,module,exports){
(function (global){
/**
* lodash (Custom Build) <https://lodash.com/>
* Build: `lodash modularize exports="npm" -o ./`
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;
/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
funcTag = '[object Function]',
genTag = '[object GeneratorFunction]',
stringTag = '[object String]';
/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();
/**
* A faster alternative to `Function#apply`, this function invokes `func`
* with the `this` binding of `thisArg` and the arguments of `args`.
*
* @private
* @param {Function} func The function to invoke.
* @param {*} thisArg The `this` binding of `func`.
* @param {Array} args The arguments to invoke `func` with.
* @returns {*} Returns the result of `func`.
*/
function apply(func, thisArg, args) {
switch (args.length) {
case 0: return func.call(thisArg);
case 1: return func.call(thisArg, args[0]);
case 2: return func.call(thisArg, args[0], args[1]);
case 3: return func.call(thisArg, args[0], args[1], args[2]);
}
return func.apply(thisArg, args);
}
/**
* The base implementation of `_.property` without support for deep paths.
*
* @private
* @param {string} key The key of the property to get.
* @returns {Function} Returns the new accessor function.
*/
function baseProperty(key) {
return function(object) {
return object == null ? undefined : object[key];
};
}
/**
* The base implementation of `_.times` without support for iteratee shorthands
* or max array length checks.
*
* @private
* @param {number} n The number of times to invoke `iteratee`.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns the array of results.
*/
function baseTimes(n, iteratee) {
var index = -1,
result = Array(n);
while (++index < n) {
result[index] = iteratee(index);
}
return result;
}
/**
* Converts `iterator` to an array.
*
* @private
* @param {Object} iterator The iterator to convert.
* @returns {Array} Returns the converted array.
*/
function iteratorToArray(iterator) {
var data,
result = [];
while (!(data = iterator.next()).done) {
result.push(data.value);
}
return result;
}
/** Used for built-in method references. */
var objectProto = Object.prototype;
/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;
/**
* Used to resolve the
* [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
* of values.
*/
var objectToString = objectProto.toString;
/** Built-in value references. */
var Reflect = root.Reflect,
enumerate = Reflect ? Reflect.enumerate : undefined,
propertyIsEnumerable = objectProto.propertyIsEnumerable;
/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;
/**
* Used by `_.defaults` to customize its `_.assignIn` use.
*
* @private
* @param {*} objValue The destination value.
* @param {*} srcValue The source value.
* @param {string} key The key of the property to assign.
* @param {Object} object The parent object of `objValue`.
* @returns {*} Returns the value to assign.
*/
function assignInDefaults(objValue, srcValue, key, object) {
if (objValue === undefined ||
(eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {
return srcValue;
}
return objValue;
}
/**
* Assigns `value` to `key` of `object` if the existing value is not equivalent
* using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
* for equality comparisons.
*
* @private
* @param {Object} object The object to modify.
* @param {string} key The key of the property to assign.
* @param {*} value The value to assign.
*/
function assignValue(object, key, value) {
var objValue = object[key];
if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
(value === undefined && !(key in object))) {
object[key] = value;
}
}
/**
* The base implementation of `_.keysIn` which doesn't skip the constructor
* property of prototypes or treat sparse arrays as dense.
*
* @private
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
*/
function baseKeysIn(object) {
object = object == null ? object : Object(object);
var result = [];
for (var key in object) {
result.push(key);
}
return result;
}
// Fallback for IE < 9 with es6-shim.
if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {
baseKeysIn = function(object) {
return iteratorToArray(enumerate(object));
};
}
/**
* The base implementation of `_.rest` which doesn't validate or coerce arguments.
*
* @private
* @param {Function} func The function to apply a rest parameter to.
* @param {number} [start=func.length-1] The start position of the rest parameter.
* @returns {Function} Returns the new function.
*/
function baseRest(func, start) {
start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
return function() {
var args = arguments,
index = -1,
length = nativeMax(args.length - start, 0),
array = Array(length);
while (++index < length) {
array[index] = args[start + index];
}
index = -1;
var otherArgs = Array(start + 1);
while (++index < start) {
otherArgs[index] = args[index];
}
otherArgs[start] = array;
return apply(func, this, otherArgs);
};
}
/**
* Copies properties of `source` to `object`.
*
* @private
* @param {Object} source The object to copy properties from.
* @param {Array} props The property identifiers to copy.
* @param {Object} [object={}] The object to copy properties to.
* @param {Function} [customizer] The function to customize copied values.
* @returns {Object} Returns `object`.
*/
function copyObject(source, props, object, customizer) {
object || (object = {});
var index = -1,
length = props.length;
while (++index < length) {
var key = props[index];
var newValue = customizer
? customizer(object[key], source[key], key, object, source)
: undefined;
assignValue(object, key, newValue === undefined ? source[key] : newValue);
}
return object;
}
/**
* Creates a function like `_.assign`.
*
* @private
* @param {Function} assigner The function to assign values.
* @returns {Function} Returns the new assigner function.
*/
function createAssigner(assigner) {
return baseRest(function(object, sources) {
var index = -1,
length = sources.length,
customizer = length > 1 ? sources[length - 1] : undefined,
guard = length > 2 ? sources[2] : undefined;
customizer = (assigner.length > 3 && typeof customizer == 'function')
? (length--, customizer)
: undefined;
if (guard && isIterateeCall(sources[0], sources[1], guard)) {
customizer = length < 3 ? undefined : customizer;
length = 1;
}
object = Object(object);
while (++index < length) {
var source = sources[index];
if (source) {
assigner(object, source, index, customizer);
}
}
return object;
});
}
/**
* Gets the "length" property value of `object`.
*
* **Note:** This function is used to avoid a
* [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects
* Safari on at least iOS 8.1-8.3 ARM64.
*
* @private
* @param {Object} object The object to query.
* @returns {*} Returns the "length" value.
*/
var getLength = baseProperty('length');
/**
* Creates an array of index keys for `object` values of arrays,
* `arguments` objects, and strings, otherwise `null` is returned.
*
* @private
* @param {Object} object The object to query.
* @returns {Array|null} Returns index keys, else `null`.
*/
function indexKeys(object) {
var length = object ? object.length : undefined;
if (isLength(length) &&
(isArray(object) || isString(object) || isArguments(object))) {
return baseTimes(length, String);
}
return null;
}
/**
* Checks if `value` is a valid array-like index.
*
* @private
* @param {*} value The value to check.
* @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
* @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
*/
function isIndex(value, length) {
length = length == null ? MAX_SAFE_INTEGER : length;
return !!length &&
(typeof value == 'number' || reIsUint.test(value)) &&
(value > -1 && value % 1 == 0 && value < length);
}
/**
* Checks if the given arguments are from an iteratee call.
*
* @private
* @param {*} value The potential iteratee value argument.
* @param {*} index The potential iteratee index or key argument.
* @param {*} object The potential iteratee object argument.
* @returns {boolean} Returns `true` if the arguments are from an iteratee call,
* else `false`.
*/
function isIterateeCall(value, index, object) {
if (!isObject(object)) {
return false;
}
var type = typeof index;
if (type == 'number'
? (isArrayLike(object) && isIndex(index, object.length))
: (type == 'string' && index in object)
) {
return eq(object[index], value);
}
return false;
}
/**
* Checks if `value` is likely a prototype object.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
*/
function isPrototype(value) {
var Ctor = value && value.constructor,
proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
return value === proto;
}
/**
* Performs a
* [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
* comparison between two values to determine if they are equivalent.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
* var object = { 'a': 1 };
* var other = { 'a': 1 };
*
* _.eq(object, object);
* // => true
*
* _.eq(object, other);
* // => false
*
* _.eq('a', 'a');
* // => true
*
* _.eq('a', Object('a'));
* // => false
*
* _.eq(NaN, NaN);
* // => true
*/
function eq(value, other) {
return value === other || (value !== value && other !== other);
}
/**
* Checks if `value` is likely an `arguments` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object,
* else `false`.
* @example
*
* _.isArguments(function() { return arguments; }());
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
function isArguments(value) {
// Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.
return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
(!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
}
/**
* Checks if `value` is classified as an `Array` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
* _.isArray([1, 2, 3]);
* // => true
*
* _.isArray(document.body.children);
* // => false
*
* _.isArray('abc');
* // => false
*
* _.isArray(_.noop);
* // => false
*/
var isArray = Array.isArray;
/**
* Checks if `value` is array-like. A value is considered array-like if it's
* not a function and has a `value.length` that's an integer greater than or
* equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is array-like, else `false`.
* @example
*
* _.isArrayLike([1, 2, 3]);
* // => true
*
* _.isArrayLike(document.body.children);
* // => true
*
* _.isArrayLike('abc');
* // => true
*
* _.isArrayLike(_.noop);
* // => false
*/
function isArrayLike(value) {
return value != null && isLength(getLength(value)) && !isFunction(value);
}
/**
* This method is like `_.isArrayLike` except that it also checks if `value`
* is an object.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array-like object,
* else `false`.
* @example
*
* _.isArrayLikeObject([1, 2, 3]);
* // => true
*
* _.isArrayLikeObject(document.body.children);
* // => true
*
* _.isArrayLikeObject('abc');
* // => false
*
* _.isArrayLikeObject(_.noop);
* // => false
*/
function isArrayLikeObject(value) {
return isObjectLike(value) && isArrayLike(value);
}
/**
* Checks if `value` is classified as a `Function` object.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
*
* _.isFunction(/abc/);
* // => false
*/
function isFunction(value) {
// The use of `Object#toString` avoids issues with the `typeof` operator
// in Safari 8 which returns 'object' for typed array and weak map constructors,
// and PhantomJS 1.9 which returns 'function' for `NodeList` instances.
var tag = isObject(value) ? objectToString.call(value) : '';
return tag == funcTag || tag == genTag;
}
/**
* Checks if `value` is a valid array-like length.
*
* **Note:** This function is loosely based on
* [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a valid length,
* else `false`.
* @example
*
* _.isLength(3);
* // => true
*
* _.isLength(Number.MIN_VALUE);
* // => false
*
* _.isLength(Infinity);
* // => false
*
* _.isLength('3');
* // => false
*/
function isLength(value) {
return typeof value == 'number' &&
value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(_.noop);
* // => true
*
* _.isObject(null);
* // => false
*/
function isObject(value) {
var type = typeof value;
return !!value && (type == 'object' || type == 'function');
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return !!value && typeof value == 'object';
}
/**
* Checks if `value` is classified as a `String` primitive or object.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a string, else `false`.
* @example
*
* _.isString('abc');
* // => true
*
* _.isString(1);
* // => false
*/
function isString(value) {
return typeof value == 'string' ||
(!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);
}
/**
* This method is like `_.assignIn` except that it accepts `customizer`
* which is invoked to produce the assigned values. If `customizer` returns
* `undefined`, assignment is handled by the method instead. The `customizer`
* is invoked with five arguments: (objValue, srcValue, key, object, source).
*
* **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 4.0.0
* @alias extendWith
* @category Object
* @param {Object} object The destination object.
* @param {...Object} sources The source objects.
* @param {Function} [customizer] The function to customize assigned values.
* @returns {Object} Returns `object`.
* @see _.assignWith
* @example
*
* function customizer(objValue, srcValue) {
* return _.isUndefined(objValue) ? srcValue : objValue;
* }
*
* var defaults = _.partialRight(_.assignInWith, customizer);
*
* defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
* // => { 'a': 1, 'b': 2 }
*/
var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
copyObject(source, keysIn(source), object, customizer);
});
/**
* Assigns own and inherited enumerable string keyed properties of source
* objects to the destination object for all destination properties that
* resolve to `undefined`. Source objects are applied from left to right.
* Once a property is set, additional values of the same property are ignored.
*
* **Note:** This method mutates `object`.
*
* @static
* @since 0.1.0
* @memberOf _
* @category Object
* @param {Object} object The destination object.
* @param {...Object} [sources] The source objects.
* @returns {Object} Returns `object`.
* @see _.defaultsDeep
* @example
*
* _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
* // => { 'a': 1, 'b': 2 }
*/
var defaults = baseRest(function(args) {
args.push(undefined, assignInDefaults);
return apply(assignInWith, undefined, args);
});
/**
* Creates an array of the own and inherited enumerable property names of `object`.
*
* **Note:** Non-object values are coerced to objects.
*
* @static
* @memberOf _
* @since 3.0.0
* @category Object
* @param {Object} object The object to query.
* @returns {Array} Returns the array of property names.
* @example
*
* function Foo() {
* this.a = 1;
* this.b = 2;
* }
*
* Foo.prototype.c = 3;
*
* _.keysIn(new Foo);
* // => ['a', 'b', 'c'] (iteration order is not guaranteed)
*/
function keysIn(object) {
var index = -1,
isProto = isPrototype(object),
props = baseKeysIn(object),
propsLength = props.length,
indexes = indexKeys(object),
skipIndexes = !!indexes,
result = indexes || [],
length = result.length;
while (++index < propsLength) {
var key = props[index];
if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&
!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
result.push(key);
}
}
return result;
}
module.exports = defaults;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],14:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
// cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
(function () {
try {
cachedSetTimeout = setTimeout;
} catch (e) {
cachedSetTimeout = function () {
throw new Error('setTimeout is not defined');
}
}
try {
cachedClearTimeout = clearTimeout;
} catch (e) {
cachedClearTimeout = function () {
throw new Error('clearTimeout is not defined');
}
}
} ())
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cache