UNPKG

@behance/router

Version:

A lightweight JavaScript library is built on top of route-recognizer and rsvp.js to provide an API for handling routes

601 lines (495 loc) 17.4 kB
define("backburner/queue", ["exports"], function(__exports__) { "use strict"; function Queue(daq, name, options) { this.daq = daq; this.name = name; this.options = options; this._queue = []; } Queue.prototype = { daq: null, name: null, options: null, _queue: null, push: function(target, method, args, stack) { var queue = this._queue; queue.push(target, method, args, stack); return {queue: this, target: target, method: method}; }, pushUnique: function(target, method, args, stack) { var queue = this._queue, currentTarget, currentMethod, i, l; for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === target && currentMethod === method) { queue[i+2] = args; // replace args queue[i+3] = stack; // replace stack return {queue: this, target: target, method: method}; // TODO: test this code path } } this._queue.push(target, method, args, stack); return {queue: this, target: target, method: method}; }, // TODO: remove me, only being used for Ember.run.sync flush: function() { var queue = this._queue, options = this.options, before = options && options.before, after = options && options.after, target, method, args, stack, i, l = queue.length; if (l && before) { before(); } for (i = 0; i < l; i += 4) { target = queue[i]; method = queue[i+1]; args = queue[i+2]; stack = queue[i+3]; // Debugging assistance // TODO: error handling if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } } if (l && after) { after(); } // check if new items have been added if (queue.length > l) { this._queue = queue.slice(l); this.flush(); } else { this._queue.length = 0; } }, cancel: function(actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { queue.splice(i, 4); return true; } } // if not found in current queue // could be in the queue that is being flushed queue = this._queueBeingFlushed; if (!queue) { return; } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { // don't mess with array during flush // just nullify the method queue[i+1] = null; return true; } } } }; __exports__.Queue = Queue; }); define("backburner/deferred_action_queues", ["backburner/queue","exports"], function(__dependency1__, __exports__) { "use strict"; var Queue = __dependency1__.Queue; function DeferredActionQueues(queueNames, options) { var queues = this.queues = {}; this.queueNames = queueNames = queueNames || []; var queueName; for (var i = 0, l = queueNames.length; i < l; i++) { queueName = queueNames[i]; queues[queueName] = new Queue(this, queueName, options[queueName]); } } DeferredActionQueues.prototype = { queueNames: null, queues: null, schedule: function(queueName, target, method, args, onceFlag, stack) { var queues = this.queues, queue = queues[queueName]; if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } if (onceFlag) { return queue.pushUnique(target, method, args, stack); } else { return queue.push(target, method, args, stack); } }, flush: function() { var queues = this.queues, queueNames = this.queueNames, queueName, queue, queueItems, priorQueueNameIndex, queueNameIndex = 0, numberOfQueues = queueNames.length; outerloop: while (queueNameIndex < numberOfQueues) { queueName = queueNames[queueNameIndex]; queue = queues[queueName]; queueItems = queue._queueBeingFlushed = queue._queue.slice(); queue._queue = []; var options = queue.options, before = options && options.before, after = options && options.after, target, method, args, stack, queueIndex = 0, numberOfQueueItems = queueItems.length; if (numberOfQueueItems && before) { before(); } while (queueIndex < numberOfQueueItems) { target = queueItems[queueIndex]; method = queueItems[queueIndex+1]; args = queueItems[queueIndex+2]; stack = queueItems[queueIndex+3]; // Debugging assistance if (typeof method === 'string') { method = target[method]; } // method could have been nullified / canceled during flush if (method) { // TODO: error handling if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } } queueIndex += 4; } queue._queueBeingFlushed = null; if (numberOfQueueItems && after) { after(); } if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { queueNameIndex = priorQueueNameIndex; continue outerloop; } queueNameIndex++; } } }; function indexOfPriorQueueWithActions(daq, currentQueueIndex) { var queueName, queue; for (var i = 0, l = currentQueueIndex; i <= l; i++) { queueName = daq.queueNames[i]; queue = daq.queues[queueName]; if (queue._queue.length) { return i; } } return -1; } __exports__.DeferredActionQueues = DeferredActionQueues; }); define("backburner", ["backburner/deferred_action_queues","exports"], function(__dependency1__, __exports__) { "use strict"; var DeferredActionQueues = __dependency1__.DeferredActionQueues; var slice = [].slice, pop = [].pop, throttlers = [], debouncees = [], timers = [], autorun, laterTimer, laterTimerExpiresAt, global = this, NUMBER = /\d+/; function isCoercableNumber(number) { return typeof number === 'number' || NUMBER.test(number); } function Backburner(queueNames, options) { this.queueNames = queueNames; this.options = options || {}; if (!this.options.defaultQueue) { this.options.defaultQueue = queueNames[0]; } this.instanceStack = []; } Backburner.prototype = { queueNames: null, options: null, currentInstance: null, instanceStack: null, begin: function() { var onBegin = this.options && this.options.onBegin, previousInstance = this.currentInstance; if (previousInstance) { this.instanceStack.push(previousInstance); } this.currentInstance = new DeferredActionQueues(this.queueNames, this.options); if (onBegin) { onBegin(this.currentInstance, previousInstance); } }, end: function() { var onEnd = this.options && this.options.onEnd, currentInstance = this.currentInstance, nextInstance = null; try { currentInstance.flush(); } finally { this.currentInstance = null; if (this.instanceStack.length) { nextInstance = this.instanceStack.pop(); this.currentInstance = nextInstance; } if (onEnd) { onEnd(currentInstance, nextInstance); } } }, run: function(target, method /*, args */) { var ret; this.begin(); if (!method) { method = target; target = null; } if (typeof method === 'string') { method = target[method]; } // Prevent Safari double-finally. var finallyAlreadyCalled = false; try { if (arguments.length > 2) { ret = method.apply(target, slice.call(arguments, 2)); } else { ret = method.call(target); } } finally { if (!finallyAlreadyCalled) { finallyAlreadyCalled = true; this.end(); } } return ret; }, defer: function(queueName, target, method /* , args */) { if (!method) { method = target; target = null; } if (typeof method === 'string') { method = target[method]; } var stack = this.DEBUG ? new Error().stack : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, deferOnce: function(queueName, target, method /* , args */) { if (!method) { method = target; target = null; } if (typeof method === 'string') { method = target[method]; } var stack = this.DEBUG ? new Error().stack : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function() { var args = slice.call(arguments); var length = args.length; var method, wait, target; var self = this; var methodOrTarget, methodOrWait, methodOrArgs; if (length === 0) { return; } else if (length === 1) { method = args.shift(); wait = 0; } else if (length === 2) { methodOrTarget = args[0]; methodOrWait = args[1]; if (typeof methodOrWait === 'function' || typeof methodOrTarget[methodOrWait] === 'function') { target = args.shift(); method = args.shift(); wait = 0; } else if (isCoercableNumber(methodOrWait)) { method = args.shift(); wait = args.shift(); } else { method = args.shift(); wait = 0; } } else { var last = args[args.length - 1]; if (isCoercableNumber(last)) { wait = args.pop(); } methodOrTarget = args[0]; methodOrArgs = args[1]; if (typeof methodOrArgs === 'function' || (typeof methodOrArgs === 'string' && methodOrTarget !== null && methodOrArgs in methodOrTarget)) { target = args.shift(); method = args.shift(); } else { method = args.shift(); } } var executeAt = (+new Date()) + parseInt(wait, 10); if (typeof method === 'string') { method = target[method]; } function fn() { method.apply(target, args); } // find position to insert - TODO: binary search var i, l; for (i = 0, l = timers.length; i < l; i += 2) { if (executeAt < timers[i]) { break; } } timers.splice(i, 0, executeAt, fn); if (laterTimer && laterTimerExpiresAt < executeAt) { return fn; } if (laterTimer) { clearTimeout(laterTimer); laterTimer = null; } laterTimer = global.setTimeout(function() { executeTimers(self); laterTimer = null; laterTimerExpiresAt = null; }, wait); laterTimerExpiresAt = executeAt; return fn; }, throttle: function(target, method /* , args, wait */) { var self = this, args = arguments, wait = parseInt(pop.call(args), 10), throttler; for (var i = 0, l = throttlers.length; i < l; i++) { throttler = throttlers[i]; if (throttler[0] === target && throttler[1] === method) { return; } // do nothing } var timer = global.setTimeout(function() { self.run.apply(self, args); // remove throttler var index = -1; for (var i = 0, l = throttlers.length; i < l; i++) { throttler = throttlers[i]; if (throttler[0] === target && throttler[1] === method) { index = i; break; } } if (index > -1) { throttlers.splice(index, 1); } }, wait); throttlers.push([target, method, timer]); }, debounce: function(target, method /* , args, wait, [immediate] */) { var self = this, args = arguments, immediate = pop.call(args), wait, index, debouncee; if (typeof immediate === "number" || typeof immediate === "string") { wait = immediate; immediate = false; } else { wait = pop.call(args); } wait = parseInt(wait, 10); // Remove debouncee index = findDebouncee(target, method); if (index !== -1) { debouncee = debouncees[index]; debouncees.splice(index, 1); clearTimeout(debouncee[2]); } var timer = global.setTimeout(function() { if (!immediate) { self.run.apply(self, args); } index = findDebouncee(target, method); if (index) { debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { self.run.apply(self, args); } debouncees.push([target, method, timer]); }, cancelTimers: function() { var i, len; for (i = 0, len = throttlers.length; i < len; i++) { clearTimeout(throttlers[i][2]); } throttlers = []; for (i = 0, len = debouncees.length; i < len; i++) { clearTimeout(debouncees[i][2]); } debouncees = []; if (laterTimer) { clearTimeout(laterTimer); laterTimer = null; } timers = []; if (autorun) { clearTimeout(autorun); autorun = null; } }, hasTimers: function() { return !!timers.length || autorun; }, cancel: function(timer) { if (timer && typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); } else if (typeof timer === 'function') { // we're cancelling a setTimeout for (var i = 0, l = timers.length; i < l; i += 2) { if (timers[i + 1] === timer) { timers.splice(i, 2); // remove the two elements return true; } } } else { return; // timer was null or not a timer } } }; Backburner.prototype.schedule = Backburner.prototype.defer; Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; Backburner.prototype.later = Backburner.prototype.setTimeout; function createAutorun(backburner) { backburner.begin(); autorun = global.setTimeout(function() { autorun = null; backburner.end(); }); } function executeTimers(self) { var now = +new Date(), time, fns, i, l; self.run(function() { // TODO: binary search for (i = 0, l = timers.length; i < l; i += 2) { time = timers[i]; if (time > now) { break; } } fns = timers.splice(0, i); for (i = 1, l = fns.length; i < l; i += 2) { self.schedule(self.options.defaultQueue, null, fns[i]); } }); if (timers.length) { laterTimer = global.setTimeout(function() { executeTimers(self); laterTimer = null; laterTimerExpiresAt = null; }, timers[0] - now); laterTimerExpiresAt = timers[0]; } } function findDebouncee(target, method) { var debouncee, index = -1; for (var i = 0, l = debouncees.length; i < l; i++) { debouncee = debouncees[i]; if (debouncee[0] === target && debouncee[1] === method) { index = i; break; } } return index; } __exports__.Backburner = Backburner; });