<commit_before>easyXDM.transport = {
    // #ifdef debug
    /**
     * @class easyXDM.transport.ITransport
     * The interface implemented by all transport classes.

     * Only available in debug mode.
     * @namespace easyXDM.transport
     */
    ITransport: {
        /**
         * Sends the message
         * @param {String} message The message to send
         */
        postMessage: function(message){
        },
        /**
         * Breaks down the connection and tries to clean up the dom.
         */
        destroy: function(){
        }
    },
    // #endif
    /**
     * @class easyXDM.transport.BestAvailableTransport
     * @extends easyXDM.transport.ITransport
     * BestAvailableTransport is a transport class that uses the best transport available.
     * Currently it will select among PostMessageTransport and HashTransport.
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    BestAvailableTransport: function(config, onReady){
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.BestAvailableTransport.constructor");
        // #endif
        if (config.local) {
            config.channel = (config.channel) ? config.channel : "default";
        }
        else {
            var query = easyXDM.Url.Query();
            config.channel = query.channel;
            config.remote = query.endpoint;
        }
        var type = "HashTransport";
        if (window.postMessage) {
            type = "PostMessageTransport";
        }
        return new easyXDM.transport[type](config, onReady);

    },
    /**
     * @class easyXDM.transport.PostMessageTransport
     * @extends easyXDM.transport.ITransport
     * PostMessageTransport is a transport class that uses HTML5 postMessage for communication
     * http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx
     * https://developer.mozilla.org/en/DOM/window.postMessage
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    PostMessageTransport: function(config, onReady){
        if (!window.postMessage) {
            throw "This browser does not support window.postMessage";
        }
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.PostMessageTransport.constructor");
        // #endif
        var _callerWindow, _targetOrigin = easyXDM.Url.getLocation(config.remote), _window_onMessageImplementation;

        /**
         * Resolves the origin from the event object
         * @private
         * @param {Object} event The messageevent
         * @return {String} The scheme, host and port of the origin
         */
        function _getOrigin(event){
            if (event.origin) {
                // This is the HTML5 property
                return event.origin;
            }
            if (event.uri) {
                // From earlier implementations
                return easyXDM.Url.getLocation(event.uri);
            }
            if (event.domain) {
                // This is the last option and will fail if the
                // origin is not using the same schema as we are
                return location.protocol + "//" + event.domain;
            }
            throw "Unable to retrieve the origin of the event";
        }

        /**
         * Delays calling onReady until the class has been returned
         * @private
         */
        function _onReady(){
            if (onReady) {
                window.setTimeout(onReady, 5);
            }
        }

        /**
         * The main onMessage handler. This will pass on the event to the real implementation
         * @private
         * @param {Object} event The messageevent
         */
        function _window_onMessage(event){
            // #ifdef debug
            easyXDM.Debug.trace("onMessage");
            // #endif
            _window_onMessageImplementation(event);
        }
        easyXDM.DomHelper.addEventListener(window, "message", _window_onMessage);

        /**
         * This is the main implementation for the onMessage event.

         * It checks the validity of the origin and passes the message on if appropriate.
         * @private
         * @param {Object} event The messageevent
         */
        function _handleMessage(event){
            var origin = _getOrigin(event);
            // #ifdef debug
            easyXDM.Debug.trace("received message '" + event.data + "' from " + origin);
            // #endif
            if (origin == _targetOrigin && event.data.substring(0, config.channel.length + 1) == config.channel + " ") {
                config.onMessage(event.data.substring(config.channel.length + 1), origin);
            }
        }

        /**
         * Used by local to fire the onReady method.
         * After being notified by the remote, this method will replace the
         * onMessage handler with _handleMessage and fire onReady
         * @private
         * @param {Object} event The messageevent
         */
        function _waitForReady(event){
            if (event.data == config.channel + "-ready") {
                // #ifdef debug
                easyXDM.Debug.trace("firing onReady");
                // #endif
                // We use memoization to avoid having to run this check each time
                _window_onMessageImplementation = _handleMessage;
                _onReady();
            }
            // #ifdef debug
            else {
                easyXDM.Debug.trace("received unexpected message: " + event.data + ", expected " + config.channel + "-ready");
            }
            // #endif
        }
        /**
         * Destroy all that we can destroy :)
         */
        this.destroy = function(){
            // #ifdef debug
            easyXDM.Debug.trace("destroying transport");
            // #endif
            easyXDM.DomHelper.removeEventListener(window, "message", _window_onMessage);
            if (config.local) {
                _callerWindow.parentNode.removeChild(_callerWindow);
                _callerWindow = null;
            }
        };

        /**
         * Sends the message using the postMethod method available on the window object
         * @param {String} message The message to send
         */
        this.postMessage = (function(){
            // Set up the messaging differently dependin on being local or remote
            if (config.local) {
                _window_onMessageImplementation = _waitForReady;
                _callerWindow = easyXDM.DomHelper.createFrame(config.remote + "?endpoint=" + easyXDM.Url.resolveUrl(config.local) + "&channel=" + config.channel, config.container);
                return function(message){
                    // #ifdef debug
                    easyXDM.Debug.trace("sending message '" + message + "' to iframe " + _targetOrigin);
                    // #endif
                    _callerWindow.contentWindow.postMessage(config.channel + " " + message, _targetOrigin);
                };
            }
            else {
                _window_onMessageImplementation = _handleMessage;
                // #ifdef debug
                easyXDM.Debug.trace("firing onReady");
                // #endif
                window.parent.postMessage(config.channel + "-ready", _targetOrigin);
                _onReady();
                return function(message){
                    // #ifdef debug
                    easyXDM.Debug.trace("sending message '" + message + "' to parent " + _targetOrigin);
                    // #endif
                    window.parent.postMessage(config.channel + " " + message, _targetOrigin);
                };

            }
        }());
    },

    /**
     * @class easyXDM.transport.HashTransport
     * @extends easyXDM.transport.ITransport
     * HashTransport is a transport class that uses the IFrame URL Technique for communication
     * http://msdn.microsoft.com/en-us/library/bb735305.aspx
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    HashTransport: function(config, onReady){
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.HashTransport.constructor");
        // #endif
        var _timer, _pollInterval = config.interval || 300;
        var _lastMsg = "#" + config.channel, _msgNr = 0;
        var _listenerWindow = (!config.local) ? window : null, _callerWindow;
        var _remoteUrl = config.remote + ((config.local) ? ("?endpoint=" + easyXDM.Url.resolveUrl(config.local) + "&channel=" + config.channel) : "#" + config.channel);
        var _remoteOrigin = easyXDM.Url.getLocation(config.remote);

        /**
         * Checks location.hash for a new message and relays this to the receiver.
         * @private
         */
        function _checkForMessage(){
            try {
                if (_listenerWindow.location.hash && _listenerWindow.location.hash != _lastMsg) {
                    _lastMsg = _listenerWindow.location.hash;
                    // #ifdef debug
                    easyXDM.Debug.trace("received message '" + _lastMsg + "' from " + _remoteOrigin);
                    // #endif
                    config.onMessage(decodeURIComponent(_lastMsg.substring(_lastMsg.indexOf("_") + 1)), _remoteOrigin);
                }
            }
            catch (ex) {
            }
        }

        /**
         * Calls the supplied onReady method

         *  We delay this so that the the call to createChannel or createTransport will have completed.
         * @private
         */
        function _onReady(){
            if (config.local) {
                _listenerWindow = easyXDM.transport.HashTransport.getWindow(config.channel);
            }
            _timer = window.setInterval(function(){
                _checkForMessage();
            }, _pollInterval);
            if (onReady) {
                window.setTimeout(onReady, 10);
            }
        }

        /**
         * Sends a message by encoding and placing it in the hash part of _callerWindows url.
         * We include a message number so that identical messages will be read as separate messages.
         * @param {String} message The message to send
         */
        this.postMessage = function(message){
            // #ifdef debug
            easyXDM.Debug.trace("sending message '" + message + "' to " + _remoteOrigin);
            // #endif
            _callerWindow.src = _remoteUrl + "#" + (_msgNr++) + "_" + encodeURIComponent(message);
        };

        /**
         * Tries to clean up the DOM
         */
        this.destroy = function(){
            // #ifdef debug
            easyXDM.Debug.trace("destroying transport");
            // #endif
            window.clearInterval(_timer);
            _callerWindow.parentNode.removeChild(_callerWindow);
            _callerWindow = null;
        };

        if (config.local) {
            // Register onReady callback in the library so that
            // it can be called when hash.html has loaded.
            easyXDM.transport.HashTransport.registerOnReady(config.channel, _onReady);
        }
        _callerWindow = easyXDM.DomHelper.createFrame(_remoteUrl, config.container, (config.local) ? null : _onReady);
    }
};

/**
 * Contains the callbacks used to notify local that the remote end is ready
 */
easyXDM.transport.HashTransport.callbacks = {};
/**
 * Contains the proxy windows used to read messages from remote when
 * using HashTransport.
 */
easyXDM.transport.HashTransport.windows = {};

/**
 * Register a callback that should be called when the remote end of a channel is ready
 * @param {String} channel
 * @param {Function} callback
 */
easyXDM.transport.HashTransport.registerOnReady = function(channel, callback){
    // #ifdef debug
    easyXDM.Debug.trace("registering onReady callback for channel " + channel);
    // #endif
    easyXDM.transport.HashTransport.callbacks[channel] = callback;
};

/**
 * Notify that a channel is ready and register a window to be used for reading messages
 * for on the channel.
 * @param {String} channel
 * @param {Window} contentWindow
 */
easyXDM.transport.HashTransport.channelReady = function(channel, contentWindow){
    easyXDM.transport.HashTransport.windows[channel] = contentWindow;
    // #ifdef debug
    easyXDM.Debug.trace("executing onReady callback for channel " + channel);
    // #endif
    var fn = easyXDM.transport.HashTransport.callbacks[channel];
    if (fn) {
        fn();
        delete easyXDM.transport.HashTransport.callbacks[channel];
    }

};

/**
 * Returns the window associated with a channel
 * @param {String} channel
 * @return {Window} The window
 */
easyXDM.transport.HashTransport.getWindow = function(channel){
    return easyXDM.transport.HashTransport.windows[channel];
};
<commit_msg>Changed HashTransport to using onresize for triggering the listener to read the incoming message. Polling is still used when placing the iframe in a container so that we dont disturbe the layout.<commit_after>easyXDM.transport = {
    // #ifdef debug
    /**
     * @class easyXDM.transport.ITransport
     * The interface implemented by all transport classes.

     * Only available in debug mode.
     * @namespace easyXDM.transport
     */
    ITransport: {
        /**
         * Sends the message
         * @param {String} message The message to send
         */
        postMessage: function(message){
        },
        /**
         * Breaks down the connection and tries to clean up the dom.
         */
        destroy: function(){
        }
    },
    // #endif
    /**
     * @class easyXDM.transport.BestAvailableTransport
     * @extends easyXDM.transport.ITransport
     * BestAvailableTransport is a transport class that uses the best transport available.
     * Currently it will select among PostMessageTransport and HashTransport.
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    BestAvailableTransport: function(config, onReady){
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.BestAvailableTransport.constructor");
        // #endif
        if (config.local) {
            config.channel = (config.channel) ? config.channel : "default";
        }
        else {
            var query = easyXDM.Url.Query();
            config.channel = query.channel;
            config.remote = query.endpoint;
        }
        var type = "HashTransport";
        if (window.postMessage) {
            type = "PostMessageTransport";
        }
        return new easyXDM.transport[type](config, onReady);

    },
    /**
     * @class easyXDM.transport.PostMessageTransport
     * @extends easyXDM.transport.ITransport
     * PostMessageTransport is a transport class that uses HTML5 postMessage for communication
     * http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx
     * https://developer.mozilla.org/en/DOM/window.postMessage
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    PostMessageTransport: function(config, onReady){
        if (!window.postMessage) {
            throw "This browser does not support window.postMessage";
        }
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.PostMessageTransport.constructor");
        // #endif
        var _callerWindow, _targetOrigin = easyXDM.Url.getLocation(config.remote), _window_onMessageImplementation;

        /**
         * Resolves the origin from the event object
         * @private
         * @param {Object} event The messageevent
         * @return {String} The scheme, host and port of the origin
         */
        function _getOrigin(event){
            if (event.origin) {
                // This is the HTML5 property
                return event.origin;
            }
            if (event.uri) {
                // From earlier implementations
                return easyXDM.Url.getLocation(event.uri);
            }
            if (event.domain) {
                // This is the last option and will fail if the
                // origin is not using the same schema as we are
                return location.protocol + "//" + event.domain;
            }
            throw "Unable to retrieve the origin of the event";
        }

        /**
         * Delays calling onReady until the class has been returned
         * @private
         */
        function _onReady(){
            if (onReady) {
                window.setTimeout(onReady, 5);
            }
        }

        /**
         * The main onMessage handler. This will pass on the event to the real implementation
         * @private
         * @param {Object} event The messageevent
         */
        function _window_onMessage(event){
            // #ifdef debug
            easyXDM.Debug.trace("onMessage");
            // #endif
            _window_onMessageImplementation(event);
        }
        easyXDM.DomHelper.addEventListener(window, "message", _window_onMessage);

        /**
         * This is the main implementation for the onMessage event.

         * It checks the validity of the origin and passes the message on if appropriate.
         * @private
         * @param {Object} event The messageevent
         */
        function _handleMessage(event){
            var origin = _getOrigin(event);
            // #ifdef debug
            easyXDM.Debug.trace("received message '" + event.data + "' from " + origin);
            // #endif
            if (origin == _targetOrigin && event.data.substring(0, config.channel.length + 1) == config.channel + " ") {
                config.onMessage(event.data.substring(config.channel.length + 1), origin);
            }
        }

        /**
         * Used by local to fire the onReady method.
         * After being notified by the remote, this method will replace the
         * onMessage handler with _handleMessage and fire onReady
         * @private
         * @param {Object} event The messageevent
         */
        function _waitForReady(event){
            if (event.data == config.channel + "-ready") {
                // #ifdef debug
                easyXDM.Debug.trace("firing onReady");
                // #endif
                // We use memoization to avoid having to run this check each time
                _window_onMessageImplementation = _handleMessage;
                _onReady();
            }
            // #ifdef debug
            else {
                easyXDM.Debug.trace("received unexpected message: " + event.data + ", expected " + config.channel + "-ready");
            }
            // #endif
        }
        /**
         * Destroy all that we can destroy :)
         */
        this.destroy = function(){
            // #ifdef debug
            easyXDM.Debug.trace("destroying transport");
            // #endif
            easyXDM.DomHelper.removeEventListener(window, "message", _window_onMessage);
            if (config.local) {
                _callerWindow.parentNode.removeChild(_callerWindow);
                _callerWindow = null;
            }
        };

        /**
         * Sends the message using the postMethod method available on the window object
         * @param {String} message The message to send
         */
        this.postMessage = (function(){
            // Set up the messaging differently dependin on being local or remote
            if (config.local) {
                _window_onMessageImplementation = _waitForReady;
                _callerWindow = easyXDM.DomHelper.createFrame(config.remote + "?endpoint=" + easyXDM.Url.resolveUrl(config.local) + "&channel=" + config.channel, config.container);
                return function(message){
                    // #ifdef debug
                    easyXDM.Debug.trace("sending message '" + message + "' to iframe " + _targetOrigin);
                    // #endif
                    _callerWindow.contentWindow.postMessage(config.channel + " " + message, _targetOrigin);
                };
            }
            else {
                _window_onMessageImplementation = _handleMessage;
                // #ifdef debug
                easyXDM.Debug.trace("firing onReady");
                // #endif
                window.parent.postMessage(config.channel + "-ready", _targetOrigin);
                _onReady();
                return function(message){
                    // #ifdef debug
                    easyXDM.Debug.trace("sending message '" + message + "' to parent " + _targetOrigin);
                    // #endif
                    window.parent.postMessage(config.channel + " " + message, _targetOrigin);
                };

            }
        }());
    },

    /**
     * @class easyXDM.transport.HashTransport
     * @extends easyXDM.transport.ITransport
     * HashTransport is a transport class that uses the IFrame URL Technique for communication
     * http://msdn.microsoft.com/en-us/library/bb735305.aspx
     * @constructor
     * @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
     * @param {Function} onReady A method that should be called when the transport is ready
     * @namespace easyXDM.transport
     */
    HashTransport: function(config, onReady){
        // #ifdef debug
        easyXDM.Debug.trace("easyXDM.transport.HashTransport.constructor");
        // #endif
        var _timer, _pollInterval = config.interval || 300, _poll;
        var _lastMsg = "#" + config.channel, _msgNr = 0, _listenerWindow, _callerWindow;
        var _remoteUrl = config.remote, _remoteOrigin = easyXDM.Url.getLocation(config.remote);
        if (config.local) {
            _poll = (typeof config.container !== "undefined");
            _remoteUrl += "?endpoint=" + easyXDM.Url.resolveUrl(config.local) + "&channel=" + config.channel;
            if (_poll) {
                _remoteUrl += "&poll=1";
                // #ifdef debug
                easyXDM.Debug.trace("using polling");
                // #endif
            }
        }
        else {
            _listenerWindow = window;
            _poll = (typeof easyXDM.Url.Query().poll !== "undefined");
            // #ifdef debug
            if (_poll) {
                easyXDM.Debug.trace("using polling");
            }
            // #endif

            _remoteUrl += "#" + config.channel;
        }
        /**
         * Checks location.hash for a new message and relays this to the receiver.
         * @private
         */
        function _checkForMessage(){
            try {
                if (_listenerWindow.location.hash && _listenerWindow.location.hash != _lastMsg) {
                    _lastMsg = _listenerWindow.location.hash;
                    // #ifdef debug
                    easyXDM.Debug.trace("received message '" + _lastMsg + "' from " + _remoteOrigin);
                    // #endif
                    config.onMessage(decodeURIComponent(_lastMsg.substring(_lastMsg.indexOf("_") + 1)), _remoteOrigin);
                }
            }
            catch (ex) {
            }
        }

        /**
         * Calls the supplied onReady method

         *  We delay this so that the the call to createChannel or createTransport will have completed.
         * @private
         */
        function _onReady(){
            if (config.local) {
                _listenerWindow = easyXDM.transport.HashTransport.getWindow(config.channel);
            }
            if (_poll) {
                _timer = window.setInterval(function(){
                    _checkForMessage();
                }, _pollInterval);
            }
            else {
                easyXDM.DomHelper.addEventListener(_listenerWindow, "resize", _checkForMessage);
            }
            if (onReady) {
                window.setTimeout(onReady, 10);
            }
        }

        /**
         * Sends a message by encoding and placing it in the hash part of _callerWindows url.
         * We include a message number so that identical messages will be read as separate messages.
         * @param {String} message The message to send
         */
        this.postMessage = function(message){
            // #ifdef debug
            easyXDM.Debug.trace("sending message '" + message + "' to " + _remoteOrigin);
            // #endif
            _callerWindow.src = _remoteUrl + "#" + (_msgNr++) + "_" + encodeURIComponent(message);
            if (!_poll) {
                _callerWindow.width = _callerWindow.width > 75 ? 50 : 100;
            }
        };

        /**
         * Tries to clean up the DOM
         */
        this.destroy = function(){
            // #ifdef debug
            easyXDM.Debug.trace("destroying transport");
            // #endif
            if (_poll) {
                window.clearInterval(_timer);
            }
            else {
                easyXDM.DomHelper.removeEventListener(_listenerWindow, "resize", _checkForMessage);
            }
            _callerWindow.parentNode.removeChild(_callerWindow);
            _callerWindow = null;
        };

        if (config.local) {
            // Register onReady callback in the library so that
            // it can be called when hash.html has loaded.
            easyXDM.transport.HashTransport.registerOnReady(config.channel, _onReady);
        }
        _callerWindow = easyXDM.DomHelper.createFrame(_remoteUrl, config.container, (config.local) ? null : _onReady);
    }
};

/**
 * Contains the callbacks used to notify local that the remote end is ready
 */
easyXDM.transport.HashTransport.callbacks = {};
/**
 * Contains the proxy windows used to read messages from remote when
 * using HashTransport.
 */
easyXDM.transport.HashTransport.windows = {};

/**
 * Register a callback that should be called when the remote end of a channel is ready
 * @param {String} channel
 * @param {Function} callback
 */
easyXDM.transport.HashTransport.registerOnReady = function(channel, callback){
    // #ifdef debug
    easyXDM.Debug.trace("registering onReady callback for channel " + channel);
    // #endif
    easyXDM.transport.HashTransport.callbacks[channel] = callback;
};

/**
 * Notify that a channel is ready and register a window to be used for reading messages
 * for on the channel.
 * @param {String} channel
 * @param {Window} contentWindow
 */
easyXDM.transport.HashTransport.channelReady = function(channel, contentWindow){
    easyXDM.transport.HashTransport.windows[channel] = contentWindow;
    // #ifdef debug
    easyXDM.Debug.trace("executing onReady callback for channel " + channel);
    // #endif
    var fn = easyXDM.transport.HashTransport.callbacks[channel];
    if (fn) {
        fn();
        delete easyXDM.transport.HashTransport.callbacks[channel];
    }

};

/**
 * Returns the window associated with a channel
 * @param {String} channel
 * @return {Window} The window
 */
easyXDM.transport.HashTransport.getWindow = function(channel){
    return easyXDM.transport.HashTransport.windows[channel];
};
<|endoftext|>


<commit_before>#!/usr/bin/env nodeold

var ru = require('./run-utils.js');

//=========== git_changes: gets any changes in the current folder; returns blank if none ============
var git_changes = function(folder) {
    var run = ru.run_command_sync;
    return run("git", ["status", "-uno", "--porcelain"]);
}

//=========== git_remote_changes: returns if remote changes exist, blank if none ============
var git_remote_changes = function(folder) {
    ru.run_command_quietly('git remote update');
    return ru.run_command_sync( "git", ["log","HEAD..HEAD@{u}","--oneline"]);
}

// =========== git_sync: commits, then pulls, then pushes to the default remote repo, as needed ============
var git_sync = function(folder,comment)
{
    Array.prototype.plus = function (other_array) {
        other_array.forEach(function(v) {this.push(v)}, this);
    }

    var changes = git_changes(folder);
    var remote_changes = git_remote_changes(folder);

    // If comment is anything other than blank, build a proper comment format that we can slap on the end of cmd.
    if (comment.length > 0)
    {
        comment = " -m \"" + comment + "\"";
    }

    var commit_task = [{ name: 'commit', folder: folder, cmd: 'cd ' + folder + ' && git commit -a' + comment 	}];
    var pull_task   = [{ name: 'pull'  , folder: folder, cmd: 'cd ' + folder + ' && git pull'                   }];
    var push_task   = [{ name: 'push'  , folder: folder, cmd: 'cd ' + folder + ' && git push'                   }];

    // Build tasks.
    var tasks = [];
    if (changes.length       ) { tasks.plus(commit_task); }
    if (remote_changes.length) { tasks.plus(pull_task  ); }
    if (changes.length       ) { tasks.plus(push_task  ); }

    // Build blip.
    var blip = "";
         if (changes.length && remote_changes.length) { blip = '<->'; }
    else if (changes.length                         ) { blip = '-->'; }
    else if (remote_changes.length                  ) { blip = '<--'; }

    if (tasks.length) {

        console.log(blip + ' ' + folder);

        // ru.runsteps(tasks,"quiet");
        ru.runsteps(tasks);
	}
}

// =========== git_clone: clones a repo ============
var git_clone = function(remote_repo,local_folder)
{
    var exec = require('child_process').exec;

    // Run these in order.
    // Make sure the task specifies the full target folder since these will be called async.
    // i.e., don't use process.cwd()...
    var cmd1 = 'git clone ' + remote_repo + ' ' + local_folder;

    exec(cmd1, function(error, stdout, stderr) {

        if (stderr) {

            if (stdout.length > 0 ) console.log(stdout);
            console.log(stderr);

        } else {

            console.log("cloned: " + local_folder);
        }
    });
}

// =========== svn_last_changed_rev: gets the SVN "last changed rev" for the current folder, as a string ============
var svn_last_changed_rev = function () {

    var run = ru.run_command_sync;

    var svn_info = run("svn", ["info"]);

    // extract the "Last Changed Rev"
    var regx = /^Last Changed Rev: (.*)$/gm;
    var array_result = regx.exec(svn_info);

    // return the first group result ([0] contains the whole result)
    return array_result[1];
}

// =========== svn_rev: gets the SVN current revision for the current repo, as a string ============
var svn_rev = function () {

    var ru = require('./run-utils.js');
    var run = ru.run_command_sync;

    var svn_info = run("svn", ["info"]);

    // extract the "Last Changed Rev"
    var regx = /^Revision: (.*)$/gm;
    var array_result = regx.exec(svn_info);

    // return the first group result ([0] contains the whole result)
    return array_result[1];
}

// ============ build_semantic_version: builds "next" historical semver, with validation using stored result ===============
var build_semantic_version = function (major,minor,patch,build,lastVersionFolder) {

    var fs = require('fs');
    var args = process.argv.slice(2);

    process.chdir(lastVersionFolder);

    var m  = parseInt(fs.readFileSync('major.txt', 'utf-8'));
    var n  = parseInt(fs.readFileSync('minor.txt', 'utf-8'));
    var p  = parseInt(fs.readFileSync('patch.txt', 'utf-8'));
    var b  = parseInt(fs.readFileSync('build.txt', 'utf-8'));

    // Compare to parameters
    // If different, adjust and save
    var m2 = major;
    var n2 = minor;
    var p2 = patch;
    var b2 = build;

    if (m2 != m)
    {
        // Validate
        if (
                m2 != m + 1
            ||	n2 != 0
            ||	p2 != 0
        ) {
            console.log('New major version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b2+')');
            process.exit(1);
        }

        // Reset n p
        m = m2;
        n = 0;
        p = 0;
        fs.writeFileSync('major.txt', m, 'utf-8');
        fs.writeFileSync('minor.txt', n, 'utf-8');
        fs.writeFileSync('patch.txt', p, 'utf-8');

    } else if (n2 != n) {

        // Validate
        if (
                n2 != n + 1
            ||	p2 != 0
        ) {
            console.log('New minor version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b2+')');
            process.exit(1);
        }

        // Reset p
        n = n2;
        p = 0;
        fs.writeFileSync('minor.txt', n, 'utf-8');
        fs.writeFileSync('patch.txt', p, 'utf-8');

    } else if (p2 != p) {

        // Validate
        if ( p2 != p + 1 )
        {
            console.log('New patch version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b+')');
            process.exit(1);
        }

        p = p2;
        fs.writeFileSync('patch.txt', p, 'utf-8');

    }

    // Write the new build version.
    b = b2;
    fs.writeFileSync('build.txt', b, 'utf-8');

    return m+'.'+n+'.'+p+'.'+b;
}

module.exports.git_changes = git_changes;
module.exports.git_sync = git_sync;
module.exports.git_clone = git_clone;
module.exports.svn_last_changed_rev = svn_last_changed_rev;
module.exports.svn_rev = svn_rev;
module.exports.build_semantic_version = build_semantic_version;

<commit_msg>auto-commit<commit_after>#!/usr/bin/env nodeold

var ru = require('./run-utils.js');

//=========== git_changes: gets any changes in the current folder; returns blank if none ============
var git_changes = function(folder) {
    var run = ru.run_command_sync;
    return run("git", ["status", "-uno", "--porcelain"]);
}

//=========== git_remote_changes: returns if remote changes exist, blank if none ============
var git_remote_changes = function(folder) {
    ru.run_command_quietly('git remote update');
    return ru.run_command_sync( "git", ["log","HEAD..HEAD@{u}","--oneline"]);
}

// =========== git_sync: commits, then pulls, then pushes to the default remote repo, as needed ============
var git_sync = function(folder,comment)
{
    Array.prototype.plus = function (other_array) {
        other_array.forEach(function(v) {this.push(v)}, this);
    }

    var changes = git_changes(folder);
    var remote_changes = git_remote_changes(folder);

    // If comment is anything other than blank, build a proper comment format that we can slap on the end of cmd.
    if (comment.length > 0)
    {
        comment = " -m \"" + comment + "\"";
    }

    var commit_task = [{ name: 'commit', folder: folder, cmd: 'cd ' + folder + ' && git commit -a' + comment 	}];
    var pull_task   = [{ name: 'pull'  , folder: folder, cmd: 'cd ' + folder + ' && git pull'                   }];
    var push_task   = [{ name: 'push'  , folder: folder, cmd: 'cd ' + folder + ' && git push'                   }];

    // Build tasks.
    var tasks = [];
    if (changes.length       ) { tasks.plus(commit_task); }
    if (remote_changes.length) { tasks.plus(pull_task  ); }
    if (changes.length       ) { tasks.plus(push_task  ); }

    // Build blip.
    var blip = "";
         if (changes.length && remote_changes.length) { blip = '<->'; }
    else if (changes.length                         ) { blip = '-->'; }
    else if (remote_changes.length                  ) { blip = '<--'; }
    else                                              { blip = '---'; }

    // if (tasks.length)
    {
        console.log(blip + ' ' + folder);

        // ru.runsteps(tasks,"quiet");
        ru.runsteps(tasks);
	}
}

// =========== git_clone: clones a repo ============
var git_clone = function(remote_repo,local_folder)
{
    var exec = require('child_process').exec;

    // Run these in order.
    // Make sure the task specifies the full target folder since these will be called async.
    // i.e., don't use process.cwd()...
    var cmd1 = 'git clone ' + remote_repo + ' ' + local_folder;

    exec(cmd1, function(error, stdout, stderr) {

        if (stderr) {

            if (stdout.length > 0 ) console.log(stdout);
            console.log(stderr);

        } else {

            console.log("cloned: " + local_folder);
        }
    });
}

// =========== svn_last_changed_rev: gets the SVN "last changed rev" for the current folder, as a string ============
var svn_last_changed_rev = function () {

    var run = ru.run_command_sync;

    var svn_info = run("svn", ["info"]);

    // extract the "Last Changed Rev"
    var regx = /^Last Changed Rev: (.*)$/gm;
    var array_result = regx.exec(svn_info);

    // return the first group result ([0] contains the whole result)
    return array_result[1];
}

// =========== svn_rev: gets the SVN current revision for the current repo, as a string ============
var svn_rev = function () {

    var ru = require('./run-utils.js');
    var run = ru.run_command_sync;

    var svn_info = run("svn", ["info"]);

    // extract the "Last Changed Rev"
    var regx = /^Revision: (.*)$/gm;
    var array_result = regx.exec(svn_info);

    // return the first group result ([0] contains the whole result)
    return array_result[1];
}

// ============ build_semantic_version: builds "next" historical semver, with validation using stored result ===============
var build_semantic_version = function (major,minor,patch,build,lastVersionFolder) {

    var fs = require('fs');
    var args = process.argv.slice(2);

    process.chdir(lastVersionFolder);

    var m  = parseInt(fs.readFileSync('major.txt', 'utf-8'));
    var n  = parseInt(fs.readFileSync('minor.txt', 'utf-8'));
    var p  = parseInt(fs.readFileSync('patch.txt', 'utf-8'));
    var b  = parseInt(fs.readFileSync('build.txt', 'utf-8'));

    // Compare to parameters
    // If different, adjust and save
    var m2 = major;
    var n2 = minor;
    var p2 = patch;
    var b2 = build;

    if (m2 != m)
    {
        // Validate
        if (
                m2 != m + 1
            ||	n2 != 0
            ||	p2 != 0
        ) {
            console.log('New major version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b2+')');
            process.exit(1);
        }

        // Reset n p
        m = m2;
        n = 0;
        p = 0;
        fs.writeFileSync('major.txt', m, 'utf-8');
        fs.writeFileSync('minor.txt', n, 'utf-8');
        fs.writeFileSync('patch.txt', p, 'utf-8');

    } else if (n2 != n) {

        // Validate
        if (
                n2 != n + 1
            ||	p2 != 0
        ) {
            console.log('New minor version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b2+')');
            process.exit(1);
        }

        // Reset p
        n = n2;
        p = 0;
        fs.writeFileSync('minor.txt', n, 'utf-8');
        fs.writeFileSync('patch.txt', p, 'utf-8');

    } else if (p2 != p) {

        // Validate
        if ( p2 != p + 1 )
        {
            console.log('New patch version provided incorrectly: old('+m+'.'+n+'.'+p+'.'+b+") new("+m2+'.'+n2+'.'+p2+'.'+b+')');
            process.exit(1);
        }

        p = p2;
        fs.writeFileSync('patch.txt', p, 'utf-8');

    }

    // Write the new build version.
    b = b2;
    fs.writeFileSync('build.txt', b, 'utf-8');

    return m+'.'+n+'.'+p+'.'+b;
}

module.exports.git_changes = git_changes;
module.exports.git_sync = git_sync;
module.exports.git_clone = git_clone;
module.exports.svn_last_changed_rev = svn_last_changed_rev;
module.exports.svn_rev = svn_rev;
module.exports.build_semantic_version = build_semantic_version;

<|endoftext|>


<commit_before>Ext.define('Jarvus.util.DAVClient', {
    requires: [
        'Jarvus.util.API'
    ],
    mixins: [
        'Ext.mixin.Observable'
    ],

    config: {
        /**
         * @cfg The {Ext.data.Connection} instance that will process requests
         * @required
         */
        connection: 'Jarvus.util.API',

        /**
         * @cfg Optional base URI to prepend DAV paths with
         */
        baseUri: null
    },

    /**
     * Automatically inject "connection" class into requires
     */
    onClassExtended: function(cls, data, hooks) {
        var connection = data.connection || data.config && data.config.connection,
            onBeforeClassCreated;

        if (typeof connection === 'string') {
            onBeforeClassCreated = hooks.onBeforeCreated;

            hooks.onBeforeCreated = function() {
                var me = this,
                    args = arguments;

                Ext.require(connection, function() {
                    onBeforeClassCreated.apply(me, args);
                });
            };
        }
    },

    constructor: function(config) {
        var me = this;

        me.mixins.observable.constructor.call(me, config);
    },

    /**
     * Convert "connection" class into constructor reference
     */
    applyConnection: function(connection) {
        if (typeof connection == 'string') {
            Ext.syncRequire(connection);
            connection = Ext.ClassManager.get(connection);
        }

        return connection;
    },

    applyBaseUri: function(baseUri) {
        return baseUri ? baseUri.replace(/\/*$/, '') : null;
    },

    request: function(options) {
        var url = options.url,
            baseUri = this.getBaseUri();

        if (url && baseUri) {
            options.url = baseUri + '/' + url.replace(/^\/*/, '');
        }

        return this.getConnection().request(options);
    },

    downloadFile: function(path, callback, scope) {
        return this.request({
            url: path,
            method: 'GET',
            callback: callback,
            scope: scope
        });
    },

    uploadFile: function(path, content, callback, scope) {
        return this.request({
            url: path,
            method: 'PUT',
            rawData: content,
            callback: callback,
            scope: scope
        });
    }
});<commit_msg>Support revision and additional options for downloadFile/uploadFile<commit_after>Ext.define('Jarvus.util.DAVClient', {
    requires: [
        'Jarvus.util.API'
    ],
    mixins: [
        'Ext.mixin.Observable'
    ],

    config: {
        /**
         * @cfg The {Ext.data.Connection} instance that will process requests
         * @required
         */
        connection: 'Jarvus.util.API',

        /**
         * @cfg Optional base URI to prepend DAV paths with
         */
        baseUri: null
    },

    /**
     * Automatically inject "connection" class into requires
     */
    onClassExtended: function(cls, data, hooks) {
        var connection = data.connection || data.config && data.config.connection,
            onBeforeClassCreated;

        if (typeof connection === 'string') {
            onBeforeClassCreated = hooks.onBeforeCreated;

            hooks.onBeforeCreated = function() {
                var me = this,
                    args = arguments;

                Ext.require(connection, function() {
                    onBeforeClassCreated.apply(me, args);
                });
            };
        }
    },

    constructor: function(config) {
        var me = this;

        me.mixins.observable.constructor.call(me, config);
    },

    /**
     * Convert "connection" class into constructor reference
     */
    applyConnection: function(connection) {
        if (typeof connection == 'string') {
            Ext.syncRequire(connection);
            connection = Ext.ClassManager.get(connection);
        }

        return connection;
    },

    applyBaseUri: function(baseUri) {
        return baseUri ? baseUri.replace(/\/*$/, '') : null;
    },

    request: function(options) {
        var url = options.url,
            baseUri = this.getBaseUri();

        if (url && baseUri) {
            options.url = baseUri + '/' + url.replace(/^\/*/, '');
        }

        return this.getConnection().request(options);
    },

    downloadFile: function(url, callback, scope) {
        var options = Ext.isString(url) ? { url: url } : url,
            headers = options.headers || (options.headers = {});

        if (options.revision) {
            headers['X-Revision-ID'] = options.revision;
        }

        return this.request(Ext.applyIf({
            method: 'GET',
            callback: callback,
            scope: scope
        }, options));
    },

    uploadFile: function(url, content, callback, scope) {
        var options = Ext.isString(url) ? { url: url } : url;

        return this.request(Ext.applyIf({
            method: 'PUT',
            rawData: content,
            callback: callback,
            scope: scope
        }, options));
    }
});<|endoftext|>


<commit_before>/**
 * L.Control.GeoSearch - search for an address and zoom to it's location
 * L.GeoSearch.Provider.OpenStreetMap uses openstreetmap geocoding service
 * https://github.com/smeijer/L.GeoSearch
 */

L.GeoSearch.Provider.OpenStreetMap = L.Class.extend({
    options: {},

    initialize: function(options) {
        options = L.Util.setOptions(this, options);
    },

    GetServiceUrl: function (qry) {
        var parameters = L.Util.extend({
            q: qry,
            format: 'json'
        }, this.options);

        return location.protocol
            + '//nominatim.openstreetmap.org/search'
            + L.Util.getParamString(parameters);
    },

    ParseJSON: function (data) {
        var results = [];

        for (var i = 0; i < data.length; i++) {
            var boundingBox = data[i].boundingbox,
                northEastLatLng = new L.LatLng( boundingBox[1], boundingBox[3] ),
                southWestLatLng = new L.LatLng( boundingBox[0], boundingBox[2] );

            results.push(new L.GeoSearch.Result(
                data[i].lon,
                data[i].lat,
                data[i].display_name,
                new L.LatLngBounds([
                    northEastLatLng,
                    southWestLatLng
                ]),
                data[i].address
            ));
        }

        return results;
    }
});
<commit_msg>Include OSM search result type in details object<commit_after>/**
 * L.Control.GeoSearch - search for an address and zoom to it's location
 * L.GeoSearch.Provider.OpenStreetMap uses openstreetmap geocoding service
 * https://github.com/smeijer/L.GeoSearch
 */

L.GeoSearch.Provider.OpenStreetMap = L.Class.extend({
    options: {},

    initialize: function(options) {
        options = L.Util.setOptions(this, options);
    },

    GetServiceUrl: function (qry) {
        var parameters = L.Util.extend({
            q: qry,
            format: 'json'
        }, this.options);

        return location.protocol
            + '//nominatim.openstreetmap.org/search'
            + L.Util.getParamString(parameters);
    },

    ParseJSON: function (data) {
        var results = [];

        for (var i = 0; i < data.length; i++) {
            var boundingBox = data[i].boundingbox,
                northEastLatLng = new L.LatLng( boundingBox[1], boundingBox[3] ),
                southWestLatLng = new L.LatLng( boundingBox[0], boundingBox[2] );

            if (data[i].address)
                data[i].address.type = data[i].type;

            results.push(new L.GeoSearch.Result(
                data[i].lon,
                data[i].lat,
                data[i].display_name,
                new L.LatLngBounds([
                    northEastLatLng,
                    southWestLatLng
                ]),
                data[i].address
            ));
        }

        return results;
    }
});
<|endoftext|>


<commit_before><commit_msg>[se] For bug 38754<commit_after><|endoftext|>


<commit_before>
(function (sandbox) {
    function MyAnalysis() {
        var stringMap = {};
        var stringList = [];
        var stringCount = 0;
        var lastiid = -1;
        var lastsid = -1;

        var traceWriter = new sandbox.TraceWriter("trace.log")
        //var logs = [];

        function logEvent(str) {
            traceWriter.logToFile(str+"\n");
            //@todo dump and clear the logs array once its size exceeds some constant, say 1024
        }

        function getValue(v) {
            var type = typeof v;
            if ((type === 'object' || type === 'function') && v !== null) {
                var shadowObj = sandbox.smemory.getShadowObjectOfObject(v);
                return sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj);
            } else if (type === 'string') {
                return getStringIndex(v);
            } else {
                return v;
            }
        }

        function getType(v) {
            var type = typeof v;
            if ((type === 'object' || type === 'function') && v !== null) {
                return 'O';
            } else if (type === 'string') {
                return 'S';
            } else {
                return 'P';
            }
        }

        function getStringIndex(str) {
            str = str + "";
            if (stringMap.hasOwnProperty(str)) {
                return stringMap[str];
            } else {
                stringCount++;
                stringMap[str] = stringCount;
                stringList.push(str);
                return stringCount;
            }
        }

        this.invokeFunPre = function (iid, f, base, args, isConstructor, isMethod, functionIid) {
            lastiid = iid;
            lastsid = sandbox.sid;
        };

        this.getFieldPre = function (iid, base, offset, isComputed, isOpAssign, isMethodCall) {
            lastiid = iid;
            lastsid = sandbox.sid;
        };

        this.getField = function (iid, base, offset, val, isComputed, isOpAssign, isMethodCall) {
            var objectId = sandbox.smemory.getIDFromShadowObjectOrFrame(sandbox.smemory.getShadowObjectOfObject(base));
            var shadowObj = sandbox.smemory.getShadowObject(base, offset, true);
            var ownerId = shadowObj.owner ? sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj.owner) : 0;
            if (shadowObj.isProperty) {
                logEvent('G,' + sandbox.sid + "," + iid + "," + objectId + "," + ownerId + "," + getStringIndex(offset) + "," + getValue(val) + "," + getType(val));
            }
        };

        this.putFieldPre = function (iid, base, offset, val, isComputed, isOpAssign) {
            lastiid = iid;
            lastsid = sandbox.sid;
            var objectId = sandbox.smemory.getIDFromShadowObjectOrFrame(sandbox.smemory.getShadowObjectOfObject(base));
            var shadowObj = sandbox.smemory.getShadowObject(base, offset, false);
            var ownerId = shadowObj.owner ? sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj.owner) : 0;
            if (shadowObj.isProperty) {
                logEvent('P,' + sandbox.sid + "," + iid + "," + objectId + "," + ownerId + "," + getStringIndex(offset) + "," + getValue(val) + "," + getType(val));
            }
        };

        this.read = function (iid, name, val, isGlobal, isScriptLocal) {
            var shadowFrame = sandbox.smemory.getShadowFrame(name);
            logEvent('R,' + sandbox.sid + "," + iid + "," + sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame) + "," + getStringIndex(name) + "," + getValue(val) + "," + getType(val));
        };

        this.write = function (iid, name, val, lhs, isGlobal, isScriptLocal) {
            var shadowFrame = sandbox.smemory.getShadowFrame(name);
            logEvent('W,' + sandbox.sid + "," + iid + "," + sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame) + "," + getStringIndex(name) + "," + getValue(val) + "," + getType(val));
        };

        this.functionEnter = function (iid, f, dis, args) {
            var shadowFrame = sandbox.smemory.getShadowFrame('this');
            logEvent('C,'+lastsid+","+lastiid+","+getValue(f)+","+sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame));
        };

        this.functionExit = function (iid, returnVal, wrappedExceptionVal) {
            logEvent('E');
        };

        this.endExecution = function () {
            traceWriter.stopTracing();
            var tw = new sandbox.TraceWriter("strings.json");
            tw.logToFile(JSON.stringify(stringList)+"\n");
            tw.stopTracing();
            tw = new sandbox.TraceWriter("smap.json");
            tw.logToFile(JSON.stringify(sandbox.smap)+"\n");
            tw.stopTracing();
        };

        this.runInstrumentedFunctionBody = function (iid, f, functionIid) {
            return false;
        };

        /**
         * onReady is useful if your analysis is running on node.js (i.e., via the direct.js or jalangi.js commands)
         * and needs to complete some asynchronous initialization before the instrumented program starts.  In such a
         * case, once the initialization is complete, invoke the cb function to start execution of the instrumented
         * program.
         *
         * Note that this callback is not useful in the browser, as Jalangi has no control over when the
         * instrumented program runs there.
         * @param cb
         */
        this.onReady = function (cb) {
            cb();
        };
    }

    sandbox.analysis = new MyAnalysis();
})(J$);

// node src/js/commands/jalangi.js --inlineIID --inlineSource --analysis src/js/sample_analyses/ChainedAnalyses.js --analysis src/js/runtime/SMemory.js --analysis src/js/sample_analyses/datatraces/TraceWriter.js --analysis src/js/sample_analyses/datatraces/LogData.js tests/octane/deltablue.js

<commit_msg>Logging location and value at return site for datatraces<commit_after>
(function (sandbox) {
    function MyAnalysis() {
        var stringMap = {};
        var stringList = [];
        var stringCount = 0;
        var lastiid = -1;
        var lastsid = -1;

        var traceWriter = new sandbox.TraceWriter("trace.log")
        //var logs = [];

        function logEvent(str) {
            traceWriter.logToFile(str+"\n");
            //@todo dump and clear the logs array once its size exceeds some constant, say 1024
        }

        function getValue(v) {
            var type = typeof v;
            if ((type === 'object' || type === 'function') && v !== null) {
                var shadowObj = sandbox.smemory.getShadowObjectOfObject(v);
                return sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj);
            } else if (type === 'string') {
                return getStringIndex(v);
            } else {
                return v;
            }
        }

        function getType(v) {
            var type = typeof v;
            if ((type === 'object' || type === 'function') && v !== null) {
                return 'O';
            } else if (type === 'string') {
                return 'S';
            } else {
                return 'P';
            }
        }

        function getStringIndex(str) {
            str = str + "";
            if (stringMap.hasOwnProperty(str)) {
                return stringMap[str];
            } else {
                stringCount++;
                stringMap[str] = stringCount;
                stringList.push(str);
                return stringCount;
            }
        }

        this.invokeFunPre = function (iid, f, base, args, isConstructor, isMethod, functionIid) {
            lastiid = iid;
            lastsid = sandbox.sid;
        };

        this.getFieldPre = function (iid, base, offset, isComputed, isOpAssign, isMethodCall) {
            lastiid = iid;
            lastsid = sandbox.sid;
        };

        this.getField = function (iid, base, offset, val, isComputed, isOpAssign, isMethodCall) {
            var objectId = sandbox.smemory.getIDFromShadowObjectOrFrame(sandbox.smemory.getShadowObjectOfObject(base));
            var shadowObj = sandbox.smemory.getShadowObject(base, offset, true);
            var ownerId = shadowObj.owner ? sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj.owner) : 0;
            if (shadowObj.isProperty) {
                logEvent('G,' + sandbox.sid + "," + iid + "," + objectId + "," + ownerId + "," + getStringIndex(offset) + "," + getValue(val) + "," + getType(val));
            }
        };

        this.putFieldPre = function (iid, base, offset, val, isComputed, isOpAssign) {
            lastiid = iid;
            lastsid = sandbox.sid;
            var objectId = sandbox.smemory.getIDFromShadowObjectOrFrame(sandbox.smemory.getShadowObjectOfObject(base));
            var shadowObj = sandbox.smemory.getShadowObject(base, offset, false);
            var ownerId = shadowObj.owner ? sandbox.smemory.getIDFromShadowObjectOrFrame(shadowObj.owner) : 0;
            if (shadowObj.isProperty) {
                logEvent('P,' + sandbox.sid + "," + iid + "," + objectId + "," + ownerId + "," + getStringIndex(offset) + "," + getValue(val) + "," + getType(val));
            }
        };

        this.read = function (iid, name, val, isGlobal, isScriptLocal) {
            var shadowFrame = sandbox.smemory.getShadowFrame(name);
            logEvent('R,' + sandbox.sid + "," + iid + "," + sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame) + "," + getStringIndex(name) + "," + getValue(val) + "," + getType(val));
        };

        this.write = function (iid, name, val, lhs, isGlobal, isScriptLocal) {
            var shadowFrame = sandbox.smemory.getShadowFrame(name);
            logEvent('W,' + sandbox.sid + "," + iid + "," + sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame) + "," + getStringIndex(name) + "," + getValue(val) + "," + getType(val));
        };

        this.functionEnter = function (iid, f, dis, args) {
            var shadowFrame = sandbox.smemory.getShadowFrame('this');
            logEvent('C,'+lastsid+","+lastiid+","+getValue(f)+","+sandbox.smemory.getIDFromShadowObjectOrFrame(shadowFrame));
        };

        this.functionExit = function (iid, returnVal, wrappedExceptionVal) {
            logEvent('E,' + sandbox.sid + "," + iid + "," + getValue(returnVal) + "," + getType(returnVal));
        };

        this.endExecution = function () {
            traceWriter.stopTracing();
            var tw = new sandbox.TraceWriter("strings.json");
            tw.logToFile(JSON.stringify(stringList)+"\n");
            tw.stopTracing();
            tw = new sandbox.TraceWriter("smap.json");
            tw.logToFile(JSON.stringify(sandbox.smap)+"\n");
            tw.stopTracing();
        };

        this.runInstrumentedFunctionBody = function (iid, f, functionIid) {
            return false;
        };

        /**
         * onReady is useful if your analysis is running on node.js (i.e., via the direct.js or jalangi.js commands)
         * and needs to complete some asynchronous initialization before the instrumented program starts.  In such a
         * case, once the initialization is complete, invoke the cb function to start execution of the instrumented
         * program.
         *
         * Note that this callback is not useful in the browser, as Jalangi has no control over when the
         * instrumented program runs there.
         * @param cb
         */
        this.onReady = function (cb) {
            cb();
        };
    }

    sandbox.analysis = new MyAnalysis();
})(J$);

// node src/js/commands/jalangi.js --inlineIID --inlineSource --analysis src/js/sample_analyses/ChainedAnalyses.js --analysis src/js/runtime/SMemory.js --analysis src/js/sample_analyses/datatraces/TraceWriter.js --analysis src/js/sample_analyses/datatraces/LogData.js tests/octane/deltablue.js

<|endoftext|>


<commit_before>/* globals ghOptionsObserver */

window.ghOptionsModal = (() => {
  const setup = () => {
    const observable = 'issue-card';

    ghOptionsObserver.observe(observable, (card) => {
      console.log(card);
    });
  };

  return {
    setup
  };
})();
<commit_msg>Modal code<commit_after>/* globals ghOptionsObserver */

window.ghOptionsModal = (() => {
  const spinnerHtml = '<img src=\'https://assets-cdn.github.com/images/spinners/octocat-spinner-64.gif\'>';

  const bind = (card) => {
    const $overlay = $('#gh-projects-options-modal-overlay');
    const $modal = $('#gh-projects-options-modal');

    $(card).find('h5 a').on('click', (event) => {
      if (event.target.href) {
        event.preventDefault();
        $modal.html(spinnerHtml);

        $.get(event.target.href, (data) => {
          $modal.html(data);
        });

        $overlay.show();
      }
    });
  };

  const setup = () => {
    $('body').prepend(Handlebars.templates['modal']());

    const $overlay = $('#gh-projects-options-modal-overlay');
    const modal = '#gh-projects-options-modal';

    $overlay.on('click', (event) => {
      const $target = $(event.target);

      if ($target.closest(modal).length) {
        return true;
      } else {
        $overlay.hide();
      }
    });

    ghOptionsObserver.observe('issue-card', bind);
  };

  return {
    setup
  };
})();
<|endoftext|>


<commit_before>class SomaPlayerPopup {
  constructor() {
    this.findElements()
    this.handleLinks()
    this.hookupMenu()
    this.applyTheme()
    this.fetchSomaStations()
    this.listenForPlayback()
  }

  hookupMenu() {
    this.stationMenuToggle.addEventListener('click', () => {
      if (this.stationMenuToggle.classList.contains('disabled')) {
        return
      }

      this.toggleStationMenu()
    })

    window.addEventListener('keydown', event => this.onKeydown(event))

    window.addEventListener('click', event => {
      const dropdown = event.target.closest('.dropdown')

      if (!dropdown) {
        this.closeStationMenu()
      }
    })
  }

  onKeydown(event) {
    switch (event.key) {
      case 'ArrowDown':
        this.focusStationListItem(1)
        this.openStationMenu()
        break;
      case 'ArrowUp':
        this.focusStationListItem(-1)
        this.openStationMenu()
        break;
      case 'Enter':
        this.handleEnter(event)
        break;
    }
  }

  handleEnter(event) {
    if (this.stationMenuToggle.classList.contains('selected')) {
      event.preventDefault()

      const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
      if (focusedItem) {
        const button = focusedItem.querySelector('.station-button')
        this.playStationFromButton(button)
      }
    }
  }

  focusStationListItem(offset) {
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItems = Array.from(this.stationListEl.querySelectorAll('.station-list-item'))
    const firstListItem = listItems[0]
    const lastListItem = listItems[listItems.length - 1]
    let newFocusedItem

    if (focusedItem) {
      const index = listItems.indexOf(focusedItem)
      focusedItem.classList.remove('focused')

      if (offset > 0) {
        newFocusedItem = listItems[index + 1] || firstListItem
      } else {
        newFocusedItem = listItems[index - 1] || lastListItem
      }

    } else {
      if (offset > 0) {
        newFocusedItem = firstListItem
      } else {
        newFocusedItem = lastListItem
      }
    }

    if (newFocusedItem) {
      newFocusedItem.classList.add('focused')
      newFocusedItem.scrollIntoView()
    }
  }

  openStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.add('active')
    this.stationMenuToggle.classList.add('selected')
  }

  closeStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.remove('active')
    this.stationMenuToggle.classList.remove('selected')
  }

  toggleStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.toggle('active')
    this.stationMenuToggle.classList.toggle('selected')
  }

  listenForPlayback() {
    this.playButton.addEventListener('click', () => this.play())
    this.pauseButton.addEventListener('click', () => this.pause())
  }

  findElements() {
    this.stationMenuToggle = document.getElementById('station-menu-toggle')
    this.stationListEl = document.getElementById('station-list')
    this.stationListItemTpl = document.getElementById('station-list-item-template')
    this.playButton = document.getElementById('play')
    this.pauseButton = document.getElementById('pause')
    this.currentInfoEl = document.getElementById('currently-playing')
    this.trackListEl = document.getElementById('track-list')
    this.trackListItemTpl = document.getElementById('track-list-item-template')
    this.stationImg = document.getElementById('station-image')
  }

  insertStationOptions(stations) {
    for (const data of stations) {
      const listItem = this.createStationListItem(data)
      this.stationListEl.appendChild(listItem)
    }

    this.stationMenuToggle.classList.remove('disabled')
    this.loadCurrentInfo()
  }

  loadDefaultStations() {
    console.debug('loading default station list')
    const url = chrome.extension.getURL('defaultStations.json')
    SomaPlayerUtil.getJSON(url).
      then(defaultStations => this.insertStationOptions(defaultStations))
  }

  fetchSomaStations() {
    chrome.runtime.sendMessage({ action: 'get_stations' }, stations => {
      if (!stations || stations.length < 1) {
        const msg = { action: 'refresh_stations' }

        chrome.runtime.sendMessage(msg, (stations, error) => {
          if (error) {
            console.error('failed to fetch stations, using defaults')
            this.loadDefaultStations()
          } else {
            console.debug('fetched stations', stations)
            this.insertStationOptions(stations)
          }
        })

      } else {
        console.debug('stations already stored', stations)
        this.insertStationOptions(stations)
      }
    })
  }

  emptyTrackList() {
    while (this.trackListEl.hasChildNodes()) {
      this.trackListEl.removeChild(this.trackListEl.lastChild);
    }
  }

  listTracks(tracks) {
    this.emptyTrackList()

    for (const track of tracks) {
      const listItem = this.createTrackListItem(track)
      this.trackListEl.appendChild(listItem)
    }
  }

  onStationButtonClick(event) {
    const button = event.currentTarget
    this.playStationFromButton(button)
  }

  playStationFromButton(button) {
    const stationID = button.value
    const stationName = button.querySelector('.station-name').textContent

    button.blur()
    this.toggleStationMenu()
    this.stationChanged(stationID, stationName)
  }

  createStationListItem(data) {
    const el = this.stationListItemTpl.content.cloneNode(true)
    const button = el.querySelector('.station-button')
    const nameEl = el.querySelector('.station-name')
    const imageEl = el.querySelector('.station-image')

    imageEl.src = `station-images/${data.id}.png`
    button.value = data.id
    nameEl.textContent = data.title
    button.addEventListener('click', e => this.onStationButtonClick(e))

    return el
  }

  createTrackListItem(track) {
    const el = this.trackListItemTpl.content.cloneNode(true)
    const nameEl = el.querySelector('.track-name')
    const artistEl = el.querySelector('.artist')
    const timeEl = el.querySelector('.track-date')
    const date = new Date(track.date)

    nameEl.textContent = track.title
    artistEl.textContent = track.artist
    timeEl.textContent = this.prettyDate(date)

    return el
  }

  prettyDate(date) {
    const fullHours = date.getHours()
    const hours = fullHours - 12
    let minutes = date.getMinutes()
    if (minutes < 10) {
      minutes = `0${minutes}`
    }
    let amPM = 'am'
    if (fullHours >= 12) {
      amPM = 'pm'
    }
    return `${hours}:${minutes} ${amPM}`
  }

  hideTrackInfo() {
    this.emptyTrackList()
    this.trackListEl.classList.add('d-none')
  }

  loadCurrentInfo() {
    this.stationMenuToggle.classList.add('disabled')

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      console.debug('finished info request, info', info);
      const currentStationButton = document.querySelector(`.station-button[value="${info.station}"]`)
      if (currentStationButton) {
        this.stationMenuToggle.value = info.station
        this.stationMenuToggle.textContent = currentStationButton.textContent
      }

      if (info.paused) {
        this.stationIsPaused()
      } else {
        this.play()
      }

      this.updateStationImage(info.station)
      this.stationMenuToggle.classList.remove('disabled')
      this.listTracks(info.tracks)
    })
  }

  stationIsPlaying() {
    this.pauseButton.classList.remove('d-none')
    this.playButton.classList.add('d-none')
    this.stationMenuToggle.focus()
  }

  stationIsPaused() {
    this.pauseButton.classList.add('d-none')
    this.playButton.classList.remove('d-none')
    this.playButton.disabled = false
    this.stationMenuToggle.focus()
  }

  play() {
    const station = this.stationMenuToggle.value
    console.debug('play', station)
    this.updateStationImage(station)

    chrome.runtime.sendMessage({ action: 'play', station }, () => {
      this.stationIsPlaying()

      chrome.runtime.sendMessage({ action: 'info' }, info => {
        if (info.tracks && info.tracks.length > 0) {
          this.listTracks(info.tracks)
        } else {
          const api = new SomaAPI()
          api.getStationTracks(station).
            then(tracks => this.listTracks(tracks)).
            catch(error => console.error('failed to get tracks', error))
        }
      })
    })
  }

  pause() {
    const station = this.stationMenuToggle.value
    console.debug('pausing station', station)

    return new Promise(resolve => {
      chrome.runtime.sendMessage({ action: 'pause', station }, () => {
        this.stationIsPaused()
        this.stationMenuToggle.focus()
        resolve()
      })
    })
  }

  updateStationImage(station) {
    if (station && station.length > 0) {
      this.stationImg.src = `station-images/${station}.png`
      this.stationImg.classList.remove('d-none')
    } else {
      this.stationImg.classList.add('d-none')
    }
  }

  stationChanged(stationID, stationName) {
    if (stationID === '') {
      chrome.runtime.sendMessage({ action: 'clear' }, () => {
        this.playButton.disabled = true
        this.hideTrackInfo()
        this.pause()
      })
      return
    }

    this.stationMenuToggle.value = stationID
    this.stationMenuToggle.textContent = stationName
    this.closeStationMenu()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      const currentStation = info.station

      if (stationID !== currentStation) {
        console.debug(`station changed to ${stationID}`)
        this.playButton.disabled = false
        this.pause().then(() => this.play())
      }
    })
  }

  handleLinks() {
    const links = Array.from(document.querySelectorAll('a'));
    for (let i = 0; i < links.length; i++) {
      this.handleLink(links[i]);
    }
  }

  handleLink(link) {
    link.addEventListener('click', event => {
      event.preventDefault();
      let url;
      const href = link.href;
      const optionsSuffix = '#options';
      if (href.indexOf(optionsSuffix) === href.length - optionsSuffix.length) {
        url = chrome.extension.getURL('options.html');
      } else {
        url = href;
      }
      chrome.tabs.create({ url });
      return false;
    });
  }

  applyTheme() {
    SomaPlayerUtil.getOptions().then(opts => {
      const theme = opts.theme || 'light'
      document.body.classList.add(`theme-${theme}`)
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SomaPlayerPopup();
});
<commit_msg>Use space to toggle play/pause<commit_after>class SomaPlayerPopup {
  constructor() {
    this.findElements()
    this.handleLinks()
    this.hookupMenu()
    this.applyTheme()
    this.fetchSomaStations()
    this.listenForPlayback()
  }

  hookupMenu() {
    this.stationMenuToggle.addEventListener('click', () => {
      if (this.stationMenuToggle.classList.contains('disabled')) {
        return
      }

      this.toggleStationMenu()
    })

    window.addEventListener('keydown', event => this.onKeydown(event))

    window.addEventListener('click', event => {
      const dropdown = event.target.closest('.dropdown')

      if (!dropdown) {
        this.closeStationMenu()
      }
    })
  }

  onKeydown(event) {
    switch (event.key) {
      case 'ArrowDown':
        this.focusStationListItem(1)
        this.openStationMenu()
        break
      case 'ArrowUp':
        this.focusStationListItem(-1)
        this.openStationMenu()
        break
      case 'Enter':
        this.handleEnter(event)
        break
      case ' ':
        this.handleSpace(event)
        break
    }
  }

  handleSpace(event) {
    event.preventDefault()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      if (info.paused) {
        this.play()
      } else {
        this.pause()
      }
    })
  }

  handleEnter(event) {
    if (this.stationMenuToggle.classList.contains('selected')) {
      event.preventDefault()

      const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
      if (focusedItem) {
        const button = focusedItem.querySelector('.station-button')
        this.playStationFromButton(button)
      }
    }
  }

  focusStationListItem(offset) {
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItems = Array.from(this.stationListEl.querySelectorAll('.station-list-item'))
    const firstListItem = listItems[0]
    const lastListItem = listItems[listItems.length - 1]
    let newFocusedItem

    if (focusedItem) {
      const index = listItems.indexOf(focusedItem)
      focusedItem.classList.remove('focused')

      if (offset > 0) {
        newFocusedItem = listItems[index + 1] || firstListItem
      } else {
        newFocusedItem = listItems[index - 1] || lastListItem
      }

    } else {
      if (offset > 0) {
        newFocusedItem = firstListItem
      } else {
        newFocusedItem = lastListItem
      }
    }

    if (newFocusedItem) {
      newFocusedItem.classList.add('focused')
      newFocusedItem.scrollIntoView()
    }
  }

  openStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.add('active')
    this.stationMenuToggle.classList.add('selected')
  }

  closeStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.remove('active')
    this.stationMenuToggle.classList.remove('selected')
  }

  toggleStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    this.stationMenuToggle.blur()
    container.classList.toggle('active')
    this.stationMenuToggle.classList.toggle('selected')
  }

  listenForPlayback() {
    this.playButton.addEventListener('click', () => this.play())
    this.pauseButton.addEventListener('click', () => this.pause())
  }

  findElements() {
    this.stationMenuToggle = document.getElementById('station-menu-toggle')
    this.stationListEl = document.getElementById('station-list')
    this.stationListItemTpl = document.getElementById('station-list-item-template')
    this.playButton = document.getElementById('play')
    this.pauseButton = document.getElementById('pause')
    this.currentInfoEl = document.getElementById('currently-playing')
    this.trackListEl = document.getElementById('track-list')
    this.trackListItemTpl = document.getElementById('track-list-item-template')
    this.stationImg = document.getElementById('station-image')
  }

  insertStationOptions(stations) {
    for (const data of stations) {
      const listItem = this.createStationListItem(data)
      this.stationListEl.appendChild(listItem)
    }

    this.stationMenuToggle.classList.remove('disabled')
    this.loadCurrentInfo()
  }

  loadDefaultStations() {
    console.debug('loading default station list')
    const url = chrome.extension.getURL('defaultStations.json')
    SomaPlayerUtil.getJSON(url).
      then(defaultStations => this.insertStationOptions(defaultStations))
  }

  fetchSomaStations() {
    chrome.runtime.sendMessage({ action: 'get_stations' }, stations => {
      if (!stations || stations.length < 1) {
        const msg = { action: 'refresh_stations' }

        chrome.runtime.sendMessage(msg, (stations, error) => {
          if (error) {
            console.error('failed to fetch stations, using defaults')
            this.loadDefaultStations()
          } else {
            console.debug('fetched stations', stations)
            this.insertStationOptions(stations)
          }
        })

      } else {
        console.debug('stations already stored', stations)
        this.insertStationOptions(stations)
      }
    })
  }

  emptyTrackList() {
    while (this.trackListEl.hasChildNodes()) {
      this.trackListEl.removeChild(this.trackListEl.lastChild);
    }
  }

  listTracks(tracks) {
    this.emptyTrackList()

    for (const track of tracks) {
      const listItem = this.createTrackListItem(track)
      this.trackListEl.appendChild(listItem)
    }
  }

  onStationButtonClick(event) {
    const button = event.currentTarget
    this.playStationFromButton(button)
  }

  playStationFromButton(button) {
    const stationID = button.value
    const stationName = button.querySelector('.station-name').textContent

    button.blur()
    this.toggleStationMenu()
    this.stationChanged(stationID, stationName)
  }

  createStationListItem(data) {
    const el = this.stationListItemTpl.content.cloneNode(true)
    const button = el.querySelector('.station-button')
    const nameEl = el.querySelector('.station-name')
    const imageEl = el.querySelector('.station-image')

    imageEl.src = `station-images/${data.id}.png`
    button.value = data.id
    nameEl.textContent = data.title
    button.addEventListener('click', e => this.onStationButtonClick(e))

    return el
  }

  createTrackListItem(track) {
    const el = this.trackListItemTpl.content.cloneNode(true)
    const nameEl = el.querySelector('.track-name')
    const artistEl = el.querySelector('.artist')
    const timeEl = el.querySelector('.track-date')
    const date = new Date(track.date)

    nameEl.textContent = track.title
    artistEl.textContent = track.artist
    timeEl.textContent = this.prettyDate(date)

    return el
  }

  prettyDate(date) {
    const fullHours = date.getHours()
    const hours = fullHours - 12
    let minutes = date.getMinutes()
    if (minutes < 10) {
      minutes = `0${minutes}`
    }
    let amPM = 'am'
    if (fullHours >= 12) {
      amPM = 'pm'
    }
    return `${hours}:${minutes} ${amPM}`
  }

  hideTrackInfo() {
    this.emptyTrackList()
    this.trackListEl.classList.add('d-none')
  }

  loadCurrentInfo() {
    this.stationMenuToggle.classList.add('disabled')

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      console.debug('finished info request, info', info);
      const currentStationButton = document.querySelector(`.station-button[value="${info.station}"]`)
      if (currentStationButton) {
        this.stationMenuToggle.value = info.station
        this.stationMenuToggle.textContent = currentStationButton.textContent
      }

      if (info.paused) {
        this.stationIsPaused()
      } else {
        this.play()
      }

      this.updateStationImage(info.station)
      this.stationMenuToggle.classList.remove('disabled')
      this.listTracks(info.tracks)
    })
  }

  stationIsPlaying() {
    this.pauseButton.classList.remove('d-none')
    this.playButton.classList.add('d-none')
    this.stationMenuToggle.focus()
  }

  stationIsPaused() {
    this.pauseButton.classList.add('d-none')
    this.playButton.classList.remove('d-none')
    this.playButton.disabled = false
    this.stationMenuToggle.focus()
  }

  play() {
    const station = this.stationMenuToggle.value
    console.debug('play', station)
    this.updateStationImage(station)

    chrome.runtime.sendMessage({ action: 'play', station }, () => {
      this.stationIsPlaying()

      chrome.runtime.sendMessage({ action: 'info' }, info => {
        if (info.tracks && info.tracks.length > 0) {
          this.listTracks(info.tracks)
        } else {
          const api = new SomaAPI()
          api.getStationTracks(station).
            then(tracks => this.listTracks(tracks)).
            catch(error => console.error('failed to get tracks', error))
        }
      })
    })
  }

  pause() {
    const station = this.stationMenuToggle.value
    console.debug('pausing station', station)

    return new Promise(resolve => {
      chrome.runtime.sendMessage({ action: 'pause', station }, () => {
        this.stationIsPaused()
        this.stationMenuToggle.focus()
        resolve()
      })
    })
  }

  updateStationImage(station) {
    if (station && station.length > 0) {
      this.stationImg.src = `station-images/${station}.png`
      this.stationImg.classList.remove('d-none')
    } else {
      this.stationImg.classList.add('d-none')
    }
  }

  stationChanged(stationID, stationName) {
    if (stationID === '') {
      chrome.runtime.sendMessage({ action: 'clear' }, () => {
        this.playButton.disabled = true
        this.hideTrackInfo()
        this.pause()
      })
      return
    }

    this.stationMenuToggle.value = stationID
    this.stationMenuToggle.textContent = stationName
    this.closeStationMenu()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      const currentStation = info.station

      if (stationID !== currentStation) {
        console.debug(`station changed to ${stationID}`)
        this.playButton.disabled = false
        this.pause().then(() => this.play())
      }
    })
  }

  handleLinks() {
    const links = Array.from(document.querySelectorAll('a'));
    for (let i = 0; i < links.length; i++) {
      this.handleLink(links[i]);
    }
  }

  handleLink(link) {
    link.addEventListener('click', event => {
      event.preventDefault();
      let url;
      const href = link.href;
      const optionsSuffix = '#options';
      if (href.indexOf(optionsSuffix) === href.length - optionsSuffix.length) {
        url = chrome.extension.getURL('options.html');
      } else {
        url = href;
      }
      chrome.tabs.create({ url });
      return false;
    });
  }

  applyTheme() {
    SomaPlayerUtil.getOptions().then(opts => {
      const theme = opts.theme || 'light'
      document.body.classList.add(`theme-${theme}`)
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SomaPlayerPopup();
});
<|endoftext|>