diff --git a/app/assets/javascripts/lib/ajaxq.js b/app/assets/javascripts/lib/ajaxq.js new file mode 100644 index 00000000..7e2c9e61 --- /dev/null +++ b/app/assets/javascripts/lib/ajaxq.js @@ -0,0 +1,161 @@ +// AjaxQ jQuery Plugin +// Copyright (c) 2012 Foliotek Inc. +// MIT License +// https://github.com/Foliotek/ajaxq +// Uses CommonJS, AMD or browser globals to create a jQuery plugin. + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function ($) { + var queues = {}; + var activeReqs = {}; + + // Register an $.ajaxq function, which follows the $.ajax interface, but allows a queue name which will force only one request per queue to fire. + // opts can be the regular $.ajax settings plainObject, or a callback returning the settings object, to be evaluated just prior to the actual call to $.ajax. + $.ajaxq = function(qname, opts) { + + if (typeof opts === "undefined") { + throw ("AjaxQ: queue name is not provided"); + } + + // Will return a Deferred promise object extended with success/error/callback, so that this function matches the interface of $.ajax + var deferred = $.Deferred(), + promise = deferred.promise(); + + promise.success = promise.done; + promise.error = promise.fail; + promise.complete = promise.always; + + // Check whether options are to be evaluated at call time or not. + var deferredOpts = typeof opts === 'function'; + // Create a deep copy of the arguments, and enqueue this request. + var clonedOptions = !deferredOpts ? $.extend(true, {}, opts) : null; + enqueue(function() { + // Send off the ajax request now that the item has been removed from the queue + var jqXHR = $.ajax.apply(window, [deferredOpts ? opts() : clonedOptions]); + + // Notify the returned deferred object with the correct context when the jqXHR is done or fails + // Note that 'always' will automatically be fired once one of these are called: http://api.jquery.com/category/deferred-object/. + jqXHR.done(function() { + deferred.resolve.apply(this, arguments); + }); + jqXHR.fail(function() { + deferred.reject.apply(this, arguments); + }); + + jqXHR.always(dequeue); // make sure to dequeue the next request AFTER the done and fail callbacks are fired + + return jqXHR; + }); + + return promise; + + + // If there is no queue, create an empty one and instantly process this item. + // Otherwise, just add this item onto it for later processing. + function enqueue(cb) { + if (!queues[qname]) { + queues[qname] = []; + var xhr = cb(); + activeReqs[qname] = xhr; + } + else { + queues[qname].push(cb); + } + } + + // Remove the next callback from the queue and fire it off. + // If the queue was empty (this was the last item), delete it from memory so the next one can be instantly processed. + function dequeue() { + if (!queues[qname]) { + return; + } + var nextCallback = queues[qname].shift(); + if (nextCallback) { + var xhr = nextCallback(); + activeReqs[qname] = xhr; + } + else { + delete queues[qname]; + delete activeReqs[qname]; + } + } + }; + + // Register a $.postq and $.getq method to provide shortcuts for $.get and $.post + // Copied from jQuery source to make sure the functions share the same defaults as $.get and $.post. + $.each( [ "getq", "postq" ], function( i, method ) { + $[ method ] = function( qname, url, data, callback, type ) { + + if ( $.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return $.ajaxq(qname, { + type: method === "postq" ? "post" : "get", + url: url, + data: data, + success: callback, + dataType: type + }); + }; + }); + + var isQueueRunning = function(qname) { + return (queues.hasOwnProperty(qname) && queues[qname].length > 0) || activeReqs.hasOwnProperty(qname); + }; + + var isAnyQueueRunning = function() { + for (var i in queues) { + if (isQueueRunning(i)) return true; + } + return false; + }; + + $.ajaxq.isRunning = function(qname) { + if (qname) return isQueueRunning(qname); + else return isAnyQueueRunning(); + }; + + $.ajaxq.getActiveRequest = function(qname) { + if (!qname) throw ("AjaxQ: queue name is required"); + + return activeReqs[qname]; + }; + + $.ajaxq.abort = function(qname) { + if (!qname) throw ("AjaxQ: queue name is required"); + + var current = $.ajaxq.getActiveRequest(qname); + delete queues[qname]; + delete activeReqs[qname]; + if (current) current.abort(); + }; + + $.ajaxq.clear = function(qname) { + if (!qname) { + for (var i in queues) { + if (queues.hasOwnProperty(i)) { + queues[i] = []; + } + } + } + else { + if (queues[qname]) { + queues[qname] = []; + } + } + }; + +})); diff --git a/app/assets/javascripts/lib/jquery.ajaxq.js b/app/assets/javascripts/lib/jquery.ajaxq.js deleted file mode 100644 index 72000c76..00000000 --- a/app/assets/javascripts/lib/jquery.ajaxq.js +++ /dev/null @@ -1,301 +0,0 @@ -;(function(root) { - 'use strict'; - - var $ = root.jQuery || root.Zepto || root.$; - - if (typeof $ === 'undefined') throw 'jquery.ajaxq requires jQuery or jQuery-compatible library (e.g. Zepto.js)'; - - /** - * @type {Function} - */ - var slice = Array.prototype.slice; - - /** - * @type {Function} - */ - var noop = function() {}; - - /** - * Copy of jQuery function - * @type {Function} - */ - var isNumeric = function(obj) { - return !$.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; - } - - /** - * @type {Function} - */ - var isObject = function(obj) { - return "[object Object]" === Object.prototype.toString.call(obj); - } - - - var Request = (function (argument) { - - function Request(url, settings) { - this._aborted = false; - this._jqXHR = null; - this._calls = {}; - this._args = [url, settings]; - this._deferred = $.Deferred(); - - this._deferred.pipe = this._deferred.then; - - this.readyState = 1; - } - - var proto = Request.prototype; - - $.extend(proto, { - - // start jqXHR by calling $.ajax - run: function() { - var - deferred = this._deferred, - methodName, argsStack, i; - - if (this._jqXHR !== null) { - return this._jqXHR; - } - // clreate new jqXHR object - var - url = this._args[0], - settings = this._args[1]; - - if (isObject(url)) { - settings = url; - } else { - settings = $.extend(true, settings || {}, { - url: url - }); - } - - this._jqXHR = $.ajax.call($, settings); - - this._jqXHR.done(function() { - deferred.resolve.apply(deferred, arguments); - }); - - this._jqXHR.fail(function() { - deferred.reject.apply(deferred, arguments); - }); - - if (this._aborted) { - this._jqXHR.abort(this.statusText); - } - - // apply buffered calls - for (methodName in this._calls) { - argsStack = this._calls[methodName]; - for (var i in argsStack) { - this._jqXHR[methodName].apply(this._jqXHR, argsStack[i]); - } - } - - return this._jqXHR; - }, - - // returns original jqXHR object if it exists - // or writes to callected method to _calls and returns itself - _call: function(methodName, args) { - if (this._jqXHR !== null) { - if (typeof this._jqXHR[methodName] === 'undefined') { - return this._jqXHR; - } - return this._jqXHR[methodName].apply(this._jqXHR, args); - } - - this._calls[methodName] = this._calls[methodName] || []; - this._calls[methodName].push(args); - - return this; - }, - - // returns original jqXHR object if it exists - // or writes to callected method to _calls and returns itself - abort: function(statusText) { - if (this._jqXHR !== null) { - var - self = this, - _copyProperties = ['readyState', 'status', 'statusText'], - _return = this._jqXHR.abort.apply(this._jqXHR, arguments) || this._jqXHR; - - if (_return) { - $.each(_copyProperties, function(i, prop) { - self[prop] = _return[prop]; - }); - } - - return _return; - } - - this.statusText = statusText || 'abort'; - this.status = 0; - this.readyState = 0; - this._aborted = true; - - return this; - }, - state: function() { - if (this._jqXHR !== null) { - return this._jqXHR.state.apply(this._jqXHR, arguments); - } - return 'pending'; - } - }); - - // each method returns self object - var _chainMethods = ['setRequestHeader', 'overrideMimeType', 'statusCode', - 'done', 'fail', 'progress', 'complete', 'success', 'error', 'always' ]; - - $.each(_chainMethods, function(i, methodName) { - proto[methodName] = function() { - return this._call(methodName, slice.call(arguments)) || this._jqXHR; - } - }); - - var _nullMethods = ['getResponseHeader', 'getAllResponseHeaders']; - - $.each(_nullMethods, function(i, methodName) { - proto[methodName] = function() { - // apply original method if _jqXHR exists - if (this._jqXHR !== null) { - return this._jqXHR[methodName].apply(this, arguments); - } - - // return null if origina method does not exists - return null; - }; - }); - - var _promiseMethods = ['pipe', 'then', 'promise']; - - $.each(_promiseMethods, function(i, methodName) { - proto[methodName] = function() { - return this._deferred[methodName].apply(this._deferred, arguments); - }; - }); - - return Request; - })() - var Queue = (function() { - - var _params = {}, _queueCounter = 0; - - function _runNext(queue, request) { - var - removeIndex = _getStarted(queue).indexOf(request), - nextRequest = _getPending(queue).shift(); - - if (removeIndex !== -1) { - _getStarted(queue).splice(removeIndex, 1); - } - - if (typeof nextRequest !== 'undefined') { - nextRequest - .always($.proxy(_runNext, null, queue, nextRequest)) - .run(); - } - } - - function _ajax(queue, request) { - if (_getStarted(queue).length < _getBandwidth(queue)) { - _getStarted(queue).push(request); - request.always($.proxy(_runNext, null, queue, request)); - request.run(); - } else { - _getPending(queue).push(request) - } - } - - function _getParams(queue) { - return _params[queue.id] || (_params[queue.id] = {}); - } - - function _getParam(queue, name) { - return _getParams(queue)[name]; - } - - function _getStarted(queue) { - return _getParams(queue).started || (_getParams(queue).started = []); - } - - function _getPending(queue) { - return _getParams(queue).pending || (_getParams(queue).pending = []); - } - - function _setBandwidth(queue, bandwidth) { - if ((bandwidth = parseInt(bandwidth || 1, 10)) < 1) throw "Bandwidth can\'t be less then 1"; - _getParams(queue).bandwidth = bandwidth; - } - - function _getBandwidth(queue, bandwidth) { - return _getParams(queue).bandwidth; - } - - function Queue(bandwidth) { - if (typeof bandwidth !== 'undefined' && !isNumeric(bandwidth)) throw "number expected"; - this.id = ++_queueCounter; - _setBandwidth(this, bandwidth); - }; - - $.extend(Queue.prototype, { - ajax: function(url, settings) { - var request = new Request(url, settings); - _ajax(this, request); - return request; - }, - getJSON: function ( url, data, callback ) { - return this.get( url, data, callback, "json" ); - }, - getBandwidth: function() { - return _getBandwidth(this); - } - }); - - $.each(['get', 'post'], function(i, method) { - Queue.prototype[method] = function( url, data, callback, type ) { - // shift arguments if data argument was omitted - if ( $.isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - return this.ajax({ - url: url, - type: method, - dataType: type, - data: data, - success: callback - }); - } - }); - - return Queue; - })(); - - if (typeof $.ajaxq !== 'undefined') throw "Namespace $.ajaxq is Alread y busy."; - - var _queue = new Queue(); - - $.ajaxq = function(url, settions) { - return _queue.ajax.apply(_queue, arguments); - }; - - $.each(['get', 'post', 'getJSON'], function(i, methodName) { - $.ajaxq[methodName] = function() { - return _queue[methodName].apply(_queue, arguments); - } - }); - - $.ajaxq.Queue = function(bandwidth) { - return new Queue(bandwidth); - }; - - $.ajaxq.Request = function(url, settings) { - return new Request(url, settings); - } - -})(this); \ No newline at end of file diff --git a/frontend/src/index.js b/frontend/src/index.js index 41d34b94..86d516b6 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -3,7 +3,7 @@ import Backbone from 'backbone' // make changes to Backbone before loading Metamaps code try { Backbone.$ = window.$ } catch(err) {} -Backbone.ajax = window.$.ajaxq +Backbone.ajax = (opts) => window.$.ajaxq('backbone-ajaxq', opts) import Metamaps from './Metamaps' diff --git a/package.json b/package.json index 171aa795..3c5b4f89 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "homepage": "https://github.com/metamaps/metamaps#readme", "dependencies": { + "ajaxq": "0.0.7", "attachmediastream": "1.4.2", "autolinker": "1.4.0", "babel-cli": "6.18.0", @@ -36,8 +37,6 @@ "getScreenMedia": "git://github.com/devvmh/getScreenMedia#patch-1", "hark": "git://github.com/devvmh/hark#patch-1", "howler": "2.0.2", - "jquery": "^3.1.1", - "jquery.ajaxq": "^0.5.2", "json-loader": "0.5.4", "lodash": "4.17.2", "node-uuid": "1.4.7",