API Docs for: undefined
Show:

File: js/soon.js

/*!
 * based on setImmediate.js. https://github.com/NobleJS/setImmediate
 * Copyright (c) 2011 Barnesandnoble.com, llc and Donavon West
 * https://github.com/NobleJS/setImmediate/blob/master/MIT-LICENSE.txt
 */

/**
 * Similar to Y.later, but sooner.
 * 
 * based on setImmediate.js. https://github.com/NobleJS/setImmediate
 * 
 * Copyright (c) 2011 Barnesandnoble.com, llc and Donavon West
 * 
 * https://github.com/NobleJS/setImmediate/blob/master/MIT-LICENSE.txt
 * @module gallery-soon
 */
(function (Y) {
    'use strict';
    
    var _callbackFunctions = {},
        _string_soon = 'soon',
        _window = Y.config.win,
       
        _call,
        _soon,
        _store;
    
    /*
     * Calls a callback function by id and removes it from the list.
     * @method _call
     * @param {String} id
     * @private
     * @returns {Boolean} true if a function was called.
     */
    _call = function (id) {
        var callbackFunction = _callbackFunctions[id];
        
        if (callbackFunction) {
            callbackFunction();
            delete _callbackFunctions[id];
            return true;
        }
        
        return false;
    };
    
    /**
     * Y.soon accepts a callback function. The callback function will be called
     * once, as soon as possible, in a future turn of the JavaScript event loop.
     * If the function requires a specific execution context or arguments, wrap
     * it with Y.bind.  Y.soon returns an object with a cancel method. If the
     * cancel method is called before the callback function, the callback
     * function won't be called.
     * 
     * based on setImmediate.js. https://github.com/NobleJS/setImmediate
     * 
     * Copyright (c) 2011 Barnesandnoble.com, llc and Donavon West
     * 
     * https://github.com/NobleJS/setImmediate/blob/master/MIT-LICENSE.txt
     * @method soon
     * @for YUI
     * @param {Function} callback function
     * @returns {Object}
     * <dl>
     *     <dt>
     *         cancel
     *     </dt>
     *     <dd>
     *         If the cancel method is called before the callback function,
     *         the callback function won't be called.
     *     </dd>
     * </dl>
     */
    
    // Check for process.nextTick in Node.js.
    if (Y.UA.nodejs && typeof process !== 'undefined' && 'nextTick' in process) {
        _soon = function (callbackFunction) {
            var id = Y.guid(_string_soon),
                response = _store(id, callbackFunction);
            
            process.nextTick(function () {
                _call(id);
            });
            
            return response;
        };
    } else if (!((function () {
        // Check for a native or already polyfilled implementation of setImmediate.
        var setImmediate = 0;
        
        Y.Array.some([
            'setImmediate',
            // TODO: Determine if these prefixes are accurate.
            'mozSetImmediate',
            'msSetImmediate',
            'oSetImmediate',
            'webkitSetImmediate'
        ], function (method) {
            if (method in _window) {
                setImmediate = _window[method];
                return true;
            }
            
            return false;
        });
                
        if (setImmediate) {
            _soon = function (callbackFunction) {
                var id = Y.guid(_string_soon),
                    response = _store(id, callbackFunction);
                
                setImmediate(function () {
                    _call(id);
                });
                
                return response;
            };
        }
        
        return _soon;
    }()) || (function () {
        // Check for postMessage support but make sure we're not in a WebWorker.
        if (('postMessage' in _window) && !('importScripts' in _window)) {
            // Check if postMessage is asynchronous.
            var oldOnMessage = _window.onmessage,
                postMessage = _window.postMessage,
                postMessageIsAsynchronous = true;
                
            _window.onmessage = function () {
                postMessageIsAsynchronous = false;
            };
            
            postMessage('', '*');
            _window.onmessage = oldOnMessage;
            
            if (postMessageIsAsynchronous) {
                Y.on('message', function (eventFacade) {
                    var event = eventFacade._event;
                        
                    // Only listen to messages from this document.
                    if (event.source === _window && _call(event.data)) {
                        // Other listeners should't care about this message.
                        eventFacade.halt(true);
                    }
                });
                
                _soon = function (callbackFunction) {
                    var id = Y.guid(_string_soon),
                        response = _store(id, callbackFunction);

                    postMessage(id, '*');

                    return response;
                };
            }
        }
        
        return _soon;
    }()) || (function () {
        // Check for MessageChannel support.
        // It's very unlikely that a browser supports MessageChannel but fails the previous check for postMessage.
        // I put them in this order because in my test, postMessage was way faster than MessageChannel in the most
        // popular browsers.
        if ('MessageChannel' in _window) {
            var messageChannel = new MessageChannel(),
                port2 = messageChannel.port2;
                
            messageChannel.port1.onmessage = function (event) {
                _call(event.data);
            };
            
            _soon = function (callbackFunction) {
                var id = Y.guid(_string_soon),
                    response = _store(id, callbackFunction);
                    
                port2.postMessage(id);
                
                return response;
            };
        }
        
        return _soon;
    }()) || (function () {
        // Check for a script node's readystatechange event.
        var Node = Y.Node,
            
            scriptNode = Node.create('<script />');
        
        if ('onreadystatechange' in scriptNode.getDOMNode()) {
            Y.mix(Node.DOM_EVENTS, {
                readystatechange: true
            });
            
            _soon = function (callbackFunction) {
                var id = Y.guid(_string_soon),
                    response = _store(id, callbackFunction),
                    scriptNode = Node.create('<script />');

                scriptNode.on('readystatechange', function () {
                    _call(id);
                    scriptNode.remove(true);
                });
                
                scriptNode.appendTo('body');

                return response;
            };
        }
        
        scriptNode.destroy();
        
        return _soon;
    }()))) {
        // Fallback to Y.later when nothing else works.
        _soon = function (callbackFunction) {
            var id = Y.guid(_string_soon),
                response = _store(id, callbackFunction);

            Y.later(0, null, _call, [
                id
            ]);

            return response;
        };
    }
    
    /*
     * Stores a callback function by id and returns an object with a cancel
     * method.
     * @method _store
     * @param {String} id
     * @param {Function} callbackFunction
     * @private
     * @returns {Object}
     * <dl>
     *     <dt>
     *         cancel
     *     </dt>
     *     <dd>
     *         If the cancel method is called before the callback function, the
     *         callback function won't be called.
     *     </dd>
     * </dl>
     */
    _store = function (id, callbackFunction) {
        _callbackFunctions[id] = callbackFunction;
        
        return {
            cancel: function () {
                delete _callbackFunctions[id];
            }
        };
    };
    
    Y.soon = _soon;
}(Y));