diff --git a/src/app/components/require.config.js b/src/app/components/require.config.js index 2541ac045f0..324ef48d6df 100644 --- a/src/app/components/require.config.js +++ b/src/app/components/require.config.js @@ -43,20 +43,12 @@ require.config({ modernizr: '../vendor/modernizr-2.6.1', elasticjs: '../vendor/elasticjs/elastic-angular-client', - rq: '../vendor/rq', - setImmediate: '../vendor/setImmediate', - }, shim: { underscore: { exports: '_' }, - rq: { - deps: ['setImmediate'], - exports: 'RQ' - }, - angular: { deps: ['jquery','config'], exports: 'angular' diff --git a/src/app/panels/graphite/graphiteSrv.js b/src/app/panels/graphite/graphiteSrv.js deleted file mode 100644 index 49a5176e598..00000000000 --- a/src/app/panels/graphite/graphiteSrv.js +++ /dev/null @@ -1,87 +0,0 @@ -define([ - 'jquery', - 'rq', - 'underscore', - 'config' -], -function ($, RQ, _, config) { - 'use strict'; - - - function build_graphite_options(options) { - var clean_options = []; - var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints']; - - options['format'] = 'json'; - - $.each(options, function (key, value) { - if ($.inArray(key, graphite_options) === -1) { - return; - } - - if (key === "targets") { - $.each(value, function (index, value) { - if (!value.hide) { - clean_options.push("target=" + encodeURIComponent(value.target)); - } - }); - } else if (value !== null) { - clean_options.push(key + "=" + encodeURIComponent(value)); - } - }); - return clean_options; - } - - function loadGraphiteData(options) - { - return function (requestion) { - var graphOptions = { - from: $.plot.formatDate(options.range.from, '%H%:%M_%Y%m%d'), - until: $.plot.formatDate(options.range.to, '%H%:%M_%Y%m%d'), - targets: options.targets, - maxDataPoints: options.maxDataPoints - }; - - var graphiteParameters = build_graphite_options(graphOptions); - getGraphiteData(graphiteParameters) - .done(function(data) { - requestion(data); - }) - .fail(function() { - requestion(null, 'Error in ajax call to graphite'); - }); - }; - } - - function getGraphiteData(parameters) { - return $.ajax({ - accepts: { text: 'application/json' }, - cache: false, - dataType: 'json', - url: config.graphiteUrl + '/render/', - type: "POST", - data: parameters.join('&') - }); - } - - function match(targets, graphiteTargetStr) { - var found = targets[0]; - - for (var i = 0; i < targets.length; i++) { - if (targets[i].target === graphiteTargetStr) { - found = targets[i]; - break; - } - if(targets[i].target.match("'" + graphiteTargetStr + "'")) { - found = targets[i]; - } - } - - return found; - } - - return { - loadGraphiteData: loadGraphiteData, - match: match - }; -}); \ No newline at end of file diff --git a/src/app/panels/graphite/module.js b/src/app/panels/graphite/module.js index cb9ddfe2301..d2cb5236ccb 100644 --- a/src/app/panels/graphite/module.js +++ b/src/app/panels/graphite/module.js @@ -19,8 +19,6 @@ define([ 'kbn', 'moment', './timeSeries', - './graphiteSrv', - 'rq', 'jquery.flot', 'jquery.flot.events', 'jquery.flot.selection', @@ -29,14 +27,15 @@ define([ 'jquery.flot.stack', 'jquery.flot.stackpercent' ], -function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) { +function (angular, app, $, _, kbn, moment, timeSeries) { 'use strict'; var module = angular.module('kibana.panels.graphite', []); app.useModule(module); - module.controller('graphite', function($scope, $rootScope, filterSrv) { + module.controller('graphite', function($scope, $rootScope, filterSrv, graphiteSrv, $timeout) { + $scope.panelMeta = { modals : [ { @@ -211,7 +210,7 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) { $scope.init = function() { - $scope.openConfigureModal(); + //$scope.openConfigureModal(); // Hide view options by default $scope.options = false; @@ -313,84 +312,71 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) { var range = $scope.get_time_range(); var interval = $scope.get_interval(range); - var graphiteLoadOptions = { + var graphiteQuery = { range: range, targets: $scope.panel.targets, maxDataPoints: $scope.panel.span * 50 }; - var result = RQ.sequence([ - graphiteSrv.loadGraphiteData(graphiteLoadOptions), - $scope.receiveGraphiteData(range, interval) - ]); - - result(function (data, failure) { - $scope.panelMeta.loading = false; - - if (failure || !data) { - $scope.panel.error = 'Failed to do fetch graphite data: ' + failure; - $scope.$apply(); - return; - } - - $scope.$apply(); - - // Tell the histogram directive to render. - $scope.$emit('render', data); - }); - + return graphiteSrv.query(graphiteQuery) + .then(function(results) { + $scope.panelMeta.loading = false; + var data = $scope.receiveGraphiteData(results, range, interval) + $scope.$emit('render', data); + }) + .then(null, function(err) { + $scope.panel.error = err.message || "Graphite HTTP Request Error"; + }); }; - $scope.receiveGraphiteData = function(range, interval) { + $scope.receiveGraphiteData = function(results, range, interval) { + var results = results.data; + $scope.legend = []; + var data = []; - return function receive_graphite_data_requestor(requestion, results) { - $scope.legend = []; - var data = []; + if(results.length === 0 ) { + throw { message: 'no data in response from graphite' }; + } - if(results.length === 0 ) { - requestion('no data in response from graphite'); - } + //console.log('Data from graphite:', results); + //console.log('nr datapoints from graphite: %d', results[0].datapoints.length); - //console.log('Data from graphite:', results); - //console.log('nr datapoints from graphite: %d', results[0].datapoints.length); + var tsOpts = { + interval: interval, + start_date: range && range.from, + end_date: range && range.to, + fill_style: 'no' + }; - var tsOpts = { - interval: interval, - start_date: range && range.from, - end_date: range && range.to, - fill_style: 'no' - }; - - _.each(results, function(targetData) { - var time_series = new timeSeries.ZeroFilled(tsOpts); - - _.each(targetData.datapoints, function(valueArray) { - if (valueArray[0]) { - time_series.addValue(valueArray[1] * 1000, valueArray[0]); - } - }); - - var target = graphiteSrv.match($scope.panel.targets, targetData.target); - - var seriesInfo = { - alias: targetData.target, - color: $scope.colors[data.length], - enable: true, - yaxis: target.yaxis || 1 - }; - - $scope.legend.push(seriesInfo); - - data.push({ - info: seriesInfo, - time_series: time_series, - yaxis: target.yaxis || 1 - }); + _.each(results, function(targetData) { + var time_series = new timeSeries.ZeroFilled(tsOpts); + _.each(targetData.datapoints, function(valueArray) { + if (valueArray[0]) { + time_series.addValue(valueArray[1] * 1000, valueArray[0]); + } }); - requestion(data); - }; + var target = graphiteSrv.match($scope.panel.targets, targetData.target); + + var seriesInfo = { + alias: targetData.target, + color: $scope.colors[data.length], + enable: true, + yaxis: target.yaxis || 1 + }; + + $scope.legend.push(seriesInfo); + + data.push({ + info: seriesInfo, + time_series: time_series, + yaxis: target.yaxis || 1 + }); + + }); + + return data; }; $scope.add_target = function() { @@ -413,7 +399,7 @@ function (angular, app, $, _, kbn, moment, timeSeries, graphiteSrv, RQ) { closeEditMode(); } - setImmediate(function() { + $timeout(function() { $scope.$emit('render'); }); }); diff --git a/src/app/services/all.js b/src/app/services/all.js index 9581337d6b8..4b174d0c91c 100644 --- a/src/app/services/all.js +++ b/src/app/services/all.js @@ -4,6 +4,7 @@ define([ './filterSrv', './timer', './panelMove', + './graphite/graphiteSrv', './esVersion', './keyboardManager', ], diff --git a/src/app/services/graphite/graphiteSrv.js b/src/app/services/graphite/graphiteSrv.js index 682e5ea11f4..52f288ebea9 100644 --- a/src/app/services/graphite/graphiteSrv.js +++ b/src/app/services/graphite/graphiteSrv.js @@ -1,7 +1,86 @@ define([ + 'angular', + 'underscore', + 'jquery', + 'config' ], -function () { +function (angular, _, $, config) { + 'use strict'; + var module = angular.module('kibana.services'); + module.service('graphiteSrv', function($http) { + + this.query = function(options) { + var graphOptions = { + from: $.plot.formatDate(options.range.from, '%H%:%M_%Y%m%d'), + until: $.plot.formatDate(options.range.to, '%H%:%M_%Y%m%d'), + targets: options.targets, + maxDataPoints: options.maxDataPoints + }; + + var params = buildGraphitePostParams(graphOptions); + + var url = config.graphiteUrl + '/render/'; + return $http({ + method: 'POST', + url: url, + data: params.join('&'), + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }); + }; + + this.match = function(targets, graphiteTargetStr) { + var found = targets[0]; + + for (var i = 0; i < targets.length; i++) { + if (targets[i].target === graphiteTargetStr) { + found = targets[i]; + break; + } + if(targets[i].target.match("'" + graphiteTargetStr + "'")) { + found = targets[i]; + } + } + + return found; + }; + + /*function getGraphiteData(parameters) { + return $.ajax({ + accepts: { text: 'application/json' }, + cache: false, + dataType: 'json', + url: config.graphiteUrl + '/render/', + type: "POST", + data: parameters.join('&') + }); + } +*/ + function buildGraphitePostParams(options) { + var clean_options = []; + var graphite_options = ['target', 'targets', 'from', 'until', 'rawData', 'format', 'maxDataPoints']; + + options['format'] = 'json'; + + $.each(options, function (key, value) { + if ($.inArray(key, graphite_options) === -1) { + return; + } + + if (key === "targets") { + $.each(value, function (index, value) { + if (!value.hide) { + clean_options.push("target=" + encodeURIComponent(value.target)); + } + }); + } else if (value !== null) { + clean_options.push(key + "=" + encodeURIComponent(value)); + } + }); + return clean_options; + } + + }); }); \ No newline at end of file diff --git a/src/vendor/rq.js b/src/vendor/rq.js deleted file mode 100644 index afeec23f0f3..00000000000 --- a/src/vendor/rq.js +++ /dev/null @@ -1,588 +0,0 @@ -/* - rq.js - - Douglas Crockford - 2013-10-11 - Public Domain - -This package uses four kinds of functions: - requestor - requestion - quash - requestory - - -requestor(requestion [, initial]) - may return a quash function - - A requestor is a function that makes a request. Such a request need not - be satisified immediately. It is likely that the request will not be - satisified until some future turn. Requestors provide a means of dealing - with future activities without blocking. - - A requestor is a function that takes a requestion function as its first - parameter, and optionally an initial value as its second parameter. The - requestor uses the requestion to report its result. A requestor may - optionally return a quash function that might be used to cancel the - request, triggering the requestion function with a failure result. - - The initial parameter contains a value that may be used to initialize the - request. It is provided specifically for RQ.sequence, but it may be passed - to any requestor. - - -requestion(success, failure) - returns undefined - - A requestion function is a continuation or callback. It is used to deliver - the result of a request. A requestion takes two arguments: success and - failure. If the request succeeds, then the result will be passed to the - requestion function as the success parameter, and the failure parameter - will be undefined. If the request fails, then the requestion function will - be passed the reason as the failure parameter. If failure is undefined, - then the request succeeded. If failure is any other value, then the request - failed. - - -quash(reason) - returns undefined - - If a request is likely to be expensive to satisfy, the requestor may - optionally return a quash function that would allow the request to be - cancelled. A requestor is not required to return a quash function, and - the quash function will not be guaranteed to cancel the request. The - quash's reason argument may become the requestion's failure argument. - - -requestory([arguments]) - returns a requestor function - - A requestory is a factory function that produces a requestor function. A - requestory function will usually take parameters that will customize or - specialize a request. It is possible to write requestor functions by hand, - but it is usually easier to generate them with requestories. - - -The RQ object contains some requestory functions that permit the composition of -requestors: - - RQ.fallback(requestors, milliseconds) - RQ.race(requestors, milliseconds) - RQ.parallel(requestors, optionals, milliseconds, tilliseconds) - RQ.sequence(requestors, milliseconds) - -Each of these four requestory functions returns a requestor function that -returns a quash function. - - -RQ.fallback(requestors, milliseconds) - - RQ.fallback returns a requestor function that will call the first element - in the requestors array. If that is ultimately successful, its value will - be passed to the requestion. But if it fails, the next element will be - called, and so on. If none of the elements are successful, then the - fallback fails. If any succeeds, then the fallback succeeds. - - If the optional milliseconds argument is supplied, then if a request is not - successful in the allotted time, then the fallback fails, and the pending - requestor is cancelled. - - -RQ.race(requestors [, milliseconds]) - - RQ.race returns a requestor that starts all of the functions in the - requestors array in parallel. Its result is the result of the first of - those requestors to successfully finish (all of the other requestors are - cancelled). If all of those requestors fail, then the race fails. - - If the optional milliseconds argument is supplied, then if no requestor has - been successful in the allotted time, then the race fails, and all pending - requestors are cancelled. - - -RQ.parallel(requestors [, milliseconds]) -RQ.parallel(requestors, optionals [, milliseconds, [tilliseconds]]) - - RQ.parallel returns a requestor that processes many requestors in parallel, - producing an array of all of the successful results. It can take two arrays - of requests: Those that are required to produce results, and those that may - optionally produce results. Each of the optional requestors has until all - of the required requestors have finished, or until the optional - tilliseconds timer has expired. - - The result maps the requestors and optionals into a single array. The - value produced by the first element of the requestors array provides the - first element of the result. - - If the optional milliseconds argument is supplied, then if all of the - required requestors are not successful in the allotted time, then the - parallel fails. If there are no required requestors, and if at least one - optional requestor is successful within the allotted time, then the - parallel succeeds. - - -RQ.sequence(requestors [, milliseconds]) - - RQ.sequence returns a requestor that processes each element of the - requestors array one at a time. Each will be passed the result of the - previous. If all succeed, then the sequence succeeds, having the result of - the last of the requestors. If any fail, then the sequence fails. - - If the optional milliseconds argument is supplied, then if all of the - requestors have not all completed in the allotted time, then the sequence - fails and the pending requestor is cancelled. -*/ - -/*global - clearTimeout, setImmediate, setTimeout -*/ - -/*properties - array, evidence, fallback, freeze, forEach, index, isArray, length, - message, method, milliseconds, name, parallel, race, sequence, value -*/ - -var RQ = (function () { - 'use strict'; - - function expired(method, milliseconds) { - -// Make an expired exception. - - return { - name: "expired", - method: method, - message: "expired after " + milliseconds, - milliseconds: milliseconds - }; - } - - function check(method, requestors, milliseconds, optionals, tilliseconds) { - -// Verify that the arguments are typed properly. - - function is_function(value, index, array) { - if (typeof value !== 'function') { - var e = new TypeError("not a function"); - e.array = array; - e.index = index; - e.method = method; - e.value = value; - throw e; - } - } - -// requestors must be an array of functions, and it may be empty only if -// optionals is present. - - if (optionals === undefined) { - if (!Array.isArray(requestors) || requestors.length === 0) { - throw new TypeError(method + " requestors"); - } - } else { - if (requestors && !Array.isArray(requestors)) { - throw new TypeError(method + " requestors"); - } - if (!Array.isArray(optionals) || optionals.length === 0) { - throw new TypeError(method + " optionals"); - } - optionals.forEach(is_function); - } - requestors.forEach(is_function); - if (milliseconds && - (typeof milliseconds !== 'number' || milliseconds < 0)) { - throw new TypeError(method + " milliseconds"); - } - if (tilliseconds && - (typeof tilliseconds !== 'number' || tilliseconds < 0)) { - throw new TypeError(method + " tilliseconds"); - } - } - - function check_requestion(method, requestion, initial) { - if (typeof requestion !== 'function') { - throw new TypeError(method + " requestion"); - } - if (initial !== null && typeof initial === 'object') { - Object.freeze(initial); - } - } - - return { - fallback : function fallback(requestors, milliseconds) { - -// RQ.fallback takes an array of requestor functions, and returns a requestor -// that will call them each in order until it finds a successful outcome. - -// If all of the requestor functions fail, then the fallback fails. If the time -// expires, then work in progress is cancelled. - - check("RQ.fallack", requestors, milliseconds); - return function requestor(requestion, initial) { - var cancel, - timeout_id; - - function finish(success, failure) { - var r = requestion; - cancel = null; - if (r) { - if (timeout_id) { - clearTimeout(timeout_id); - } - requestion = null; - timeout_id = null; - return r(success, failure); - } - } - - function quash(reason) { - if (requestion && typeof cancel === 'function') { - setImmediate(cancel, reason); - } - return finish(undefined, reason || true); - } - - check_requestion("RQ.fallack", requestion, initial); - if (milliseconds) { - timeout_id = setTimeout(function () { - return quash(expired("RQ.fallback", milliseconds)); - }, milliseconds); - } - (function next(index, failure) { - if (typeof requestion === 'function') { - -// If there are no more requestors, then signal failure. - - if (index >= requestors.length) { - clearTimeout(timeout_id); - cancel = null; - return quash(failure); - } - -// If there is another requestor, call it in the next turn, passing the value -// and a requestion that will take the next step. - - var requestor = requestors[index]; - setImmediate(function () { - var once = true; - if (typeof requestion === 'function') { - cancel = requestor( - function requestion(success, failure) { - if (once) { - once = false; - cancel = null; - return failure === undefined - ? finish(success) - : next(index + 1, failure); - } - }, - initial - ); - } - }); - } - }(0)); - return quash; - }; - }, - parallel: function parallel(requestors, optionals, milliseconds, - tilliseconds) { - -// RQ.parallel takes an array of requestors, and an optional second array of -// requestors, and starts them all. It succeeds if all of the requestors in -// the first array finish successfully before the time expires. The result -// is an array collecting the results of all of the requestors. - - if (typeof optionals === 'number') { - milliseconds = optionals; - tilliseconds = undefined; - optionals = undefined; - } - check("RQ.parallel", requestors, milliseconds, optionals, - tilliseconds); - - return function requestor(requestion, initial) { - var quashes = [], - optionals_remaining, - optionals_successes = 0, - requestors_length = requestors.length, - requestors_remaining = requestors.length, - results = [], - timeout_till, - timeout_id; - - function finish(success, failure) { - var r = requestion; - if (r) { - requestion = null; - if (timeout_id) { - clearTimeout(timeout_id); - timeout_id = null; - } - if (timeout_till) { - clearTimeout(timeout_till); - timeout_till = null; - } - quashes.forEach(function (quash) { - if (typeof quash === 'function') { - return setImmediate(quash, failure); - } - }); - quashes = null; - results = null; - return r(success, failure); - } - } - - function quash(reason) { - return finish(undefined, reason || true); - } - - check_requestion("RQ.parallel", requestion, initial); - -// milliseconds, if specified, says take no longer to process this request. If -// any of the required requestors are not successful by this time, the parallel -// requestor fails. - - if (milliseconds) { - timeout_id = setTimeout(function () { - timeout_id = null; - return requestors_remaining === 0 && - (requestors_length > 0 || - optionals_successes > 0) - ? finish(results) - : quash(expired("RQ.parallel", milliseconds)); - }, milliseconds); - -// tilliseconds, if specified, gives more time for the optional requestors to -// complete. Normally, the optional requestors have until all of the required -// requestors finish. If tilliseconds is larger than milliseconds, milliseconds -// wins. - - } - if (tilliseconds) { - timeout_till = setTimeout(function () { - timeout_till = null; - if (requestors_remaining === 0) { - return finish(results); - } - }, tilliseconds); - } - if (requestors) { - requestors.forEach(function (requestor, index) { - return setImmediate(function () { - var once = true, cancel = requestor( - function requestion(success, failure) { - if (once && quashes) { - once = false; - quashes[index] = null; - if (failure !== undefined) { - return quash(failure); - } - results[index] = success; - requestors_remaining -= 1; - if (requestors_remaining === 0 && - !timeout_till) { - return finish(results); - } - } - }, - initial - ); - if (quashes && quashes[index] === undefined) { - quashes[index] = cancel; - } - }); - }); - } - if (optionals) { - optionals_remaining = optionals.length; - optionals.forEach(function (requestor, index) { - return setImmediate(function () { - var once = true, cancel = requestor( - function requestion(success, failure) { - if (once && quashes) { - once = false; - quashes[requestors_length + index] - = null; - if (failure === undefined) { - results[requestors_length + index] - = success; - optionals_successes += 1; - } - optionals_remaining -= 1; - if (optionals_remaining === 0) { - if (requestors_remaining === 0) { - return requestors_length > 0 || - optionals_successes > 0 - ? finish(results) - : quash(failure); - } - if (timeout_till) { - clearTimeout(timeout_till); - timeout_till = null; - } - } - } - }, - initial - ); - if (quashes[requestors_length + index] === - undefined) { - quashes[requestors_length + index] = cancel; - } - }); - }); - } - return quash; - }; - }, - race: function race(requestors, milliseconds) { - -// RQ.race takes an array of requestor functions. It starts them all -// immediately. The first to finish wins. A race is successful if any -// contestant is successful. It fails if all requestors fail or if the time -// expires. - - check("RQ.race", requestors, milliseconds); - return function requestor(requestion, initial) { - var quashes = [], - remaining = requestors.length, - timeout_id; - - function finish(success, failure) { - var r = requestion; - if (r) { - requestion = null; - if (timeout_id) { - clearTimeout(timeout_id); - } - quashes.forEach(function stop(quash) { - if (typeof quash === 'function') { - return setImmediate(quash); - } - }); - quashes = null; - return r(success, failure); - } - } - - function quash(reason) { - return finish(undefined, reason || true); - } - - check_requestion("RQ.race", requestion, initial); - if (milliseconds) { - timeout_id = setTimeout(function timeout_id() { - return quash(expired("RQ.race", milliseconds)); - }, milliseconds); - } - requestors.forEach(function (requestor, index) { - return setImmediate(function () { - var once = true, cancel = requestor( - function requestion(success, failure) { - if (once && quashes) { - once = false; - quashes[index] = null; - if (failure === undefined) { - return finish(success); - } - remaining -= 1; - if (remaining === 0) { - return quash(failure); - } - } - }, - initial - ); - if (quashes[index] === undefined) { - quashes[index] = cancel; - } - }); - }); - return quash; - }; - }, - sequence: function sequence(requestors, milliseconds) { - -// RQ.sequence takes an array of requestor functions, and returns a requestor -// that will call them each in order. An initial value is passed to each, which -// is the previous success result. - -// If any of the requestor functions fail, then the whole sequence fails, and -// the remaining requestors are not called. - - check("RQ.sequence", requestors, milliseconds); - return function requestor(requestion, initial) { - var cancel, - timeout_id; - - function finish(success, failure) { - var r = requestion; - cancel = null; - if (r) { - if (timeout_id) { - clearTimeout(timeout_id); - } - requestion = null; - return r(success, failure); - } - } - - function quash(reason) { - if (requestion && typeof cancel === 'function') { - setImmediate(cancel, reason); - } - return finish(undefined, reason || true); - } - - check_requestion("RQ.sequence", requestion, initial); - if (milliseconds) { - timeout_id = setTimeout(function () { - timeout_id = null; - return quash(expired("RQ.sequence", milliseconds)); - }, milliseconds); - } - (function next(index) { - var requestor, r = requestion; - if (typeof r === 'function') { - -// If there are no more requestors, then signal success. - - if (index >= requestors.length) { - if (timeout_id) { - clearTimeout(timeout_id); - } - requestion = null; - cancel = null; - return r(initial); - } - -// If there is another requestor, call it in the next turn, passing the value -// and a requestion that will take the next step. - - requestor = requestors[index]; - setImmediate(function () { - var once = true; - cancel = requestor( - function requestion(success, failure) { - if (once) { - once = false; - cancel = null; - if (failure !== undefined) { - return quash(failure); - } - initial = success; - return next(index + 1); - } - }, - initial - ); - }); - } - }(0)); - return quash; - }; - } - }; -}()); \ No newline at end of file diff --git a/src/vendor/setImmediate.js b/src/vendor/setImmediate.js deleted file mode 100644 index 886a89e88dd..00000000000 --- a/src/vendor/setImmediate.js +++ /dev/null @@ -1,218 +0,0 @@ -(function (global, undefined) { - "use strict"; - - var tasks = (function () { - function Task(handler, args) { - this.handler = handler; - this.args = args; - } - Task.prototype.run = function () { - // See steps in section 5 of the spec. - if (typeof this.handler === "function") { - // Choice of `thisArg` is not in the setImmediate spec; `undefined` is in the setTimeout spec though: - // http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html - this.handler.apply(undefined, this.args); - } else { - var scriptSource = "" + this.handler; - /*jshint evil: true */ - eval(scriptSource); - } - }; - - var nextHandle = 1; // Spec says greater than zero - var tasksByHandle = {}; - var currentlyRunningATask = false; - - return { - addFromSetImmediateArguments: function (args) { - var handler = args[0]; - var argsToHandle = Array.prototype.slice.call(args, 1); - var task = new Task(handler, argsToHandle); - - var thisHandle = nextHandle++; - tasksByHandle[thisHandle] = task; - return thisHandle; - }, - runIfPresent: function (handle) { - // From the spec: "Wait until any invocations of this algorithm started before this one have completed." - // So if we're currently running a task, we'll need to delay this invocation. - if (!currentlyRunningATask) { - var task = tasksByHandle[handle]; - if (task) { - currentlyRunningATask = true; - try { - task.run(); - } finally { - delete tasksByHandle[handle]; - currentlyRunningATask = false; - } - } - } else { - // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a - // "too much recursion" error. - global.setTimeout(function () { - tasks.runIfPresent(handle); - }, 0); - } - }, - remove: function (handle) { - delete tasksByHandle[handle]; - } - }; - }()); - - function canUseNextTick() { - // Don't get fooled by e.g. browserify environments. - return typeof process === "object" && - Object.prototype.toString.call(process) === "[object process]"; - } - - function canUseMessageChannel() { - return !!global.MessageChannel; - } - - function canUsePostMessage() { - // The test against `importScripts` prevents this implementation from being installed inside a web worker, - // where `global.postMessage` means something completely different and can't be used for this purpose. - - if (!global.postMessage || global.importScripts) { - return false; - } - - var postMessageIsAsynchronous = true; - var oldOnMessage = global.onmessage; - global.onmessage = function () { - postMessageIsAsynchronous = false; - }; - global.postMessage("", "*"); - global.onmessage = oldOnMessage; - - return postMessageIsAsynchronous; - } - - function canUseReadyStateChange() { - return "document" in global && "onreadystatechange" in global.document.createElement("script"); - } - - function installNextTickImplementation(attachTo) { - attachTo.setImmediate = function () { - var handle = tasks.addFromSetImmediateArguments(arguments); - - process.nextTick(function () { - tasks.runIfPresent(handle); - }); - - return handle; - }; - } - - function installMessageChannelImplementation(attachTo) { - var channel = new global.MessageChannel(); - channel.port1.onmessage = function (event) { - var handle = event.data; - tasks.runIfPresent(handle); - }; - attachTo.setImmediate = function () { - var handle = tasks.addFromSetImmediateArguments(arguments); - - channel.port2.postMessage(handle); - - return handle; - }; - } - - function installPostMessageImplementation(attachTo) { - // Installs an event handler on `global` for the `message` event: see - // * https://developer.mozilla.org/en/DOM/window.postMessage - // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages - - var MESSAGE_PREFIX = "com.bn.NobleJS.setImmediate" + Math.random(); - - function isStringAndStartsWith(string, putativeStart) { - return typeof string === "string" && string.substring(0, putativeStart.length) === putativeStart; - } - - function onGlobalMessage(event) { - // This will catch all incoming messages (even from other windows!), so we need to try reasonably hard to - // avoid letting anyone else trick us into firing off. We test the origin is still this window, and that a - // (randomly generated) unpredictable identifying prefix is present. - if (event.source === global && isStringAndStartsWith(event.data, MESSAGE_PREFIX)) { - var handle = event.data.substring(MESSAGE_PREFIX.length); - tasks.runIfPresent(handle); - } - } - if (global.addEventListener) { - global.addEventListener("message", onGlobalMessage, false); - } else { - global.attachEvent("onmessage", onGlobalMessage); - } - - attachTo.setImmediate = function () { - var handle = tasks.addFromSetImmediateArguments(arguments); - - // Make `global` post a message to itself with the handle and identifying prefix, thus asynchronously - // invoking our onGlobalMessage listener above. - global.postMessage(MESSAGE_PREFIX + handle, "*"); - - return handle; - }; - } - - function installReadyStateChangeImplementation(attachTo) { - attachTo.setImmediate = function () { - var handle = tasks.addFromSetImmediateArguments(arguments); - - // Create a