"use strict"; var utils = require("./utils"); module.exports = function batchProcessorMaker(options) { options = options || {}; var reporter = options.reporter; var asyncProcess = utils.getOption(options, "async", true); var autoProcess = utils.getOption(options, "auto", true); if(autoProcess && !asyncProcess) { reporter && reporter.warn("Invalid options combination. auto=true and async=false is invalid. Setting async=true."); asyncProcess = true; } var batch = Batch(); var asyncFrameHandler; var isProcessing = false; function addFunction(level, fn) { if(!isProcessing && autoProcess && asyncProcess && batch.size() === 0) { // Since this is async, it is guaranteed to be executed after that the fn is added to the batch. // This needs to be done before, since we're checking the size of the batch to be 0. processBatchAsync(); } batch.add(level, fn); } function processBatch() { // Save the current batch, and create a new batch so that incoming functions are not added into the currently processing batch. // Continue processing until the top-level batch is empty (functions may be added to the new batch while processing, and so on). isProcessing = true; while (batch.size()) { var processingBatch = batch; batch = Batch(); processingBatch.process(); } isProcessing = false; } function forceProcessBatch(localAsyncProcess) { if (isProcessing) { return; } if(localAsyncProcess === undefined) { localAsyncProcess = asyncProcess; } if(asyncFrameHandler) { cancelFrame(asyncFrameHandler); asyncFrameHandler = null; } if(localAsyncProcess) { processBatchAsync(); } else { processBatch(); } } function processBatchAsync() { asyncFrameHandler = requestFrame(processBatch); } function clearBatch() { batch = {}; batchSize = 0; topLevel = 0; bottomLevel = 0; } function cancelFrame(listener) { // var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout; var cancel = clearTimeout; return cancel(listener); } function requestFrame(callback) { // var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function(fn) { return window.setTimeout(fn, 20); }; var raf = function(fn) { return setTimeout(fn, 0); }; return raf(callback); } return { add: addFunction, force: forceProcessBatch }; }; function Batch() { var batch = {}; var size = 0; var topLevel = 0; var bottomLevel = 0; function add(level, fn) { if(!fn) { fn = level; level = 0; } if(level > topLevel) { topLevel = level; } else if(level < bottomLevel) { bottomLevel = level; } if(!batch[level]) { batch[level] = []; } batch[level].push(fn); size++; } function process() { for(var level = bottomLevel; level <= topLevel; level++) { var fns = batch[level]; for(var i = 0; i < fns.length; i++) { var fn = fns[i]; fn(); } } } function getSize() { return size; } return { add: add, process: process, size: getSize }; }