<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* The State Manager is responsible for loading, setting up and switching game states.
*
* @class Phaser.StateManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.StateManager = function (game, pending)
{
    this.game = game;

    //  Everything kept in here
    this.keys = {};
    this.states = [];

    //  Only active states are kept in here
    this.active = [];

    this._pending = [];

    if (pending)
    {
        if (Array.isArray(pending))
        {
            for (var i = 0; i < pending.length; i++)
            {
                //  The i === 0 part just starts the first State given
                this._pending.push({ key: 'default', state: pending[i], autoStart: (i === 0) });
            }
        }
        else
        {
            this._pending.push({ key: 'default', state: pending, autoStart: true });
        }
    }
};

Phaser.StateManager.prototype = {

    /**
    * The Boot handler is called by Phaser.Game when it first starts up.
    * The renderer is available by now.
    *
    * @method Phaser.StateManager#boot
    * @private
    */
    boot: function ()
    {
        // this.game.onPause.add(this.pause, this);
        // this.game.onResume.add(this.resume, this);

        console.log('StateManager.boot');

        for (var i = 0; i < this._pending.length; i++)
        {
            var entry = this._pending[i];

            this.add(entry.key, entry.state, entry.autoStart);
        }

        this._pending = [];
    },

    getKey: function (key, state)
    {
        if (!key) { key = 'default'; }

        if (state instanceof Phaser.State)
        {
            key = state.settings.key;
        }
        else if (typeof state === 'object' && state.hasOwnProperty('key'))
        {
            key = state.key;
        }

        //  By this point it's either 'default' or extracted from the State

        if (this.keys.hasOwnProperty(key))
        {
            throw new Error('Cannot add a State with duplicate key: ' + key);
        }
        else
        {
            return key;
        }
    },

    /**
    * Adds a new State into the StateManager. You must give each State a unique key by which you'll identify it.
    * The State can be either a Phaser.State object (or an object that extends it), a plain JavaScript object or a function.
    * If a function is given a new state object will be created by calling it.
    *
    * @method Phaser.StateManager#add
    * @param {string} key - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
    * @param {Phaser.State|object|function} state  - The state you want to switch to.
    * @param {boolean} [autoStart=false]  - If true the State will be started immediately after adding it.
    */
    add: function (key, state, autoStart)
    {
        if (autoStart === undefined) { autoStart = false; }

        //  if not booted, then put state into a holding pattern
        if (!this.game.isBooted)
        {
            console.log('StateManager not yet booted, adding to list');

            this._pending.push({ key: key, state: state, autoStart: autoStart });

            return;
        }

        key = this.getKey(key, state);

        var newState;

        if (state instanceof Phaser.State)
        {
            console.log('StateManager.add from instance', key);
            newState = this.createStateFromInstance(key, state);
        }
        else if (typeof state === 'object')
        {
            console.log('StateManager.add from object', key);
            newState = this.createStateFromObject(key, state);
        }
        else if (typeof state === 'function')
        {
            console.log('StateManager.add from function', key);
            newState = this.createStateFromFunction(key, state);
        }

        this.keys[key] = newState;

        this.states.push(newState);

        // window.console.dir(newState);

        if (autoStart || newState.settings.active)
        {
            if (this.game.isBooted)
            {
                this.start(key);
            }
            else
            {
                this._start.push(key);
            }
        }

        return newState;
    },

    createStateFromInstance: function (key, newState)
    {
        newState.game = this.game;

        newState.settings.key = key;

        newState.sys.init();

        if (this.game.renderType === Phaser.WEBGL)
        {
            this.createStateFrameBuffer(newState);
        }

        return newState;
    },

    createStateFromObject: function (key, state)
    {
        var newState = new Phaser.State(key);

        newState.game = this.game;

        newState.sys.init();

        if (this.game.renderType === Phaser.WEBGL)
        {
            this.createStateFrameBuffer(newState);
        }

        //  Extract callbacks or set NOOP

        if (state.hasOwnProperty('init'))
        {
            newState.init = state.init;
        }

        if (state.hasOwnProperty('preload'))
        {
            newState.preload = state.preload;
        }

        if (state.hasOwnProperty('create'))
        {
            newState.create = state.create;
        }

        if (state.hasOwnProperty('shutdown'))
        {
            newState.shutdown = state.shutdown;
        }

        newState.preUpdate = (state.hasOwnProperty('preUpdate')) ? state.preUpdate : Phaser.NOOP;
        newState.update = (state.hasOwnProperty('update')) ? state.update : Phaser.NOOP;
        newState.postUpdate = (state.hasOwnProperty('postUpdate')) ? state.postUpdate : Phaser.NOOP;
        newState.render = (state.hasOwnProperty('render')) ? state.render : Phaser.NOOP;

        return newState;
    },

    createStateFromFunction: function (key, state)
    {
        var newState = new state();

        if (newState instanceof Phaser.State)
        {
            return this.createStateFromInstance(key, newState);
        }
        else
        {
            newState.game = this.game;

            newState.settings = new Phaser.State.Settings(newState, key);

            newState.sys = new Phaser.State.Systems(newState);

            newState.sys.init();

            if (this.game.renderType === Phaser.WEBGL)
            {
                this.createStateFrameBuffer(newState);
            }

            //  Default required functions

            if (!newState.preUpdate)
            {
                newState.preUpdate = Phaser.NOOP;
            }

            if (!newState.update)
            {
                newState.update = Phaser.NOOP;
            }

            if (!newState.postUpdate)
            {
                newState.postUpdate = Phaser.NOOP;
            }

            if (!newState.render)
            {
                newState.render = Phaser.NOOP;
            }

            return newState;
        }
    },

    createStateFrameBuffer: function (newState)
    {
        var x = newState.settings.x;
        var y = newState.settings.y;

        if (newState.settings.width === -1)
        {
            newState.settings.width = this.game.width;
        }

        if (newState.settings.height === -1)
        {
            newState.settings.height = this.game.height;
        }

        var width = newState.settings.width;
        var height = newState.settings.height;

        newState.sys.fbo = this.game.renderer.createFBO(x, y, width, height);
    },

    getState: function (key)
    {
        return this.keys[key];
    },

    isActive: function (key)
    {
        var state = this.getState(key);

        return (state && state.settings.active && this.active.indexOf(state) !== -1);
    },

    start: function (key)
    {
        //  if not booted, then put state into a holding pattern
        if (!this.game.isBooted)
        {
            console.log('StateManager not yet booted, setting autoStart on pending list');

            for (var i = 0; i < this._pending.length; i++)
            {
                var entry = this._pending[i];

                if (entry.key === key)
                {
                    entry.autoStart = true;
                }
            }

            return;
        }

        var state = this.getState(key);

        if (state)
        {
            //  Already started? Nothing more to do here ...
            if (this.isActive(key))
            {
                return;
            }

            state.settings.active = true;

            this.active.push(state);

            //  + arguments
            if (state.init)
            {
                state.init.call(state);
            }

            if (state.preload)
            {
                state.sys.load.reset(true);

                state.preload.call(state, this.game);

                //  Is the loader empty?
                if (state.sys.load.totalQueuedFiles() === 0 && state.sys.load.totalQueuedPacks() === 0)
                {
                    // console.log('empty queue');
                    this.startCreate(state);
                }
                else
                {
                    // console.log('load start');

                    //  Start the loader going as we have something in the queue
                    // state.load.onLoadComplete.addOnce(this.loadComplete, this, 0, state);

                    state.sys.load.start();
                }
            }
            else
            {
                // console.log('no preload');

                //  No preload? Then there was nothing to load either
                this.startCreate(state);
            }

        }
    },

    loadComplete: function (state)
    {
        // console.log('loadComplete');

        //  Make sure to do load-update one last time before state is set to _created

        if (state.hasOwnProperty('loadUpdate'))
        {
            state.loadUpdate.call(state);
        }

        this.startCreate(state);
    },

    startCreate: function (state)
    {
        if (state.create)
        {
            state.create.call(state);
        }

        this.game.updates.running = true;
    },

    //  See if we can reduce this down to just update and render

    preUpdate: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                state.sys.children.list[c].preUpdate();
            }

            state.preUpdate();
        }
    },

    update: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            //  Invoke State Main Loop here - updating all of its systems (tweens, physics, etc)

            //  This shouldn't be called if the State is still loading
            //  Have a State.STATUS const in the Settings, dictating what is going on

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                var child = state.sys.children.list[c];

                if (child.exists)
                {
                    child.update();
                }
            }

            state.update();
        }
    },

    postUpdate: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                state.sys.children.list[c].postUpdate();
            }

            state.postUpdate();
        }
    },

    render: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            //  Can put all kinds of other checks in here, like MainLoop, FPS, etc.
            if (!state.settings.visible || state.sys.color.alpha === 0 || state.sys.children.list.length === 0)
            {
                continue;
            }

            this.game.renderer.render(state);
        }
    },

    renderChildren: function (renderer, state)
    {
        //  Populates the display list
        for (var c = 0; c < state.sys.children.list.length; c++)
        {
            var child = state.sys.children.list[c];

            child.render(renderer, child);
        }
    }

};
<commit_msg>Don't move to active until init / preload / create is over.<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* The State Manager is responsible for loading, setting up and switching game states.
*
* @class Phaser.StateManager
* @constructor
* @param {Phaser.Game} game - A reference to the currently running game.
*/
Phaser.StateManager = function (game, pending)
{
    this.game = game;

    //  Everything kept in here
    this.keys = {};
    this.states = [];

    //  Only active states are kept in here
    this.active = [];

    this._pending = [];

    if (pending)
    {
        if (Array.isArray(pending))
        {
            for (var i = 0; i < pending.length; i++)
            {
                //  The i === 0 part just starts the first State given
                this._pending.push({ key: 'default', state: pending[i], autoStart: (i === 0) });
            }
        }
        else
        {
            this._pending.push({ key: 'default', state: pending, autoStart: true });
        }
    }
};

Phaser.StateManager.prototype = {

    /**
    * The Boot handler is called by Phaser.Game when it first starts up.
    * The renderer is available by now.
    *
    * @method Phaser.StateManager#boot
    * @private
    */
    boot: function ()
    {
        // this.game.onPause.add(this.pause, this);
        // this.game.onResume.add(this.resume, this);

        console.log('StateManager.boot');

        for (var i = 0; i < this._pending.length; i++)
        {
            var entry = this._pending[i];

            this.add(entry.key, entry.state, entry.autoStart);
        }

        this._pending = [];
    },

    getKey: function (key, state)
    {
        if (!key) { key = 'default'; }

        if (state instanceof Phaser.State)
        {
            key = state.settings.key;
        }
        else if (typeof state === 'object' && state.hasOwnProperty('key'))
        {
            key = state.key;
        }

        //  By this point it's either 'default' or extracted from the State

        if (this.keys.hasOwnProperty(key))
        {
            throw new Error('Cannot add a State with duplicate key: ' + key);
        }
        else
        {
            return key;
        }
    },

    /**
    * Adds a new State into the StateManager. You must give each State a unique key by which you'll identify it.
    * The State can be either a Phaser.State object (or an object that extends it), a plain JavaScript object or a function.
    * If a function is given a new state object will be created by calling it.
    *
    * @method Phaser.StateManager#add
    * @param {string} key - A unique key you use to reference this state, i.e. "MainMenu", "Level1".
    * @param {Phaser.State|object|function} state  - The state you want to switch to.
    * @param {boolean} [autoStart=false]  - If true the State will be started immediately after adding it.
    */
    add: function (key, state, autoStart)
    {
        if (autoStart === undefined) { autoStart = false; }

        //  if not booted, then put state into a holding pattern
        if (!this.game.isBooted)
        {
            console.log('StateManager not yet booted, adding to list');

            this._pending.push({ key: key, state: state, autoStart: autoStart });

            return;
        }

        key = this.getKey(key, state);

        var newState;

        if (state instanceof Phaser.State)
        {
            console.log('StateManager.add from instance', key);
            newState = this.createStateFromInstance(key, state);
        }
        else if (typeof state === 'object')
        {
            console.log('StateManager.add from object', key);
            newState = this.createStateFromObject(key, state);
        }
        else if (typeof state === 'function')
        {
            console.log('StateManager.add from function', key);
            newState = this.createStateFromFunction(key, state);
        }

        this.keys[key] = newState;

        this.states.push(newState);

        // window.console.dir(newState);

        if (autoStart || newState.settings.active)
        {
            if (this.game.isBooted)
            {
                this.start(key);
            }
            else
            {
                this._start.push(key);
            }
        }

        return newState;
    },

    createStateFromInstance: function (key, newState)
    {
        newState.game = this.game;

        newState.settings.key = key;

        newState.sys.init();

        if (this.game.renderType === Phaser.WEBGL)
        {
            this.createStateFrameBuffer(newState);
        }

        return newState;
    },

    createStateFromObject: function (key, state)
    {
        var newState = new Phaser.State(key);

        newState.game = this.game;

        newState.sys.init();

        if (this.game.renderType === Phaser.WEBGL)
        {
            this.createStateFrameBuffer(newState);
        }

        //  Extract callbacks or set NOOP

        if (state.hasOwnProperty('init'))
        {
            newState.init = state.init;
        }

        if (state.hasOwnProperty('preload'))
        {
            newState.preload = state.preload;
        }

        if (state.hasOwnProperty('create'))
        {
            newState.create = state.create;
        }

        if (state.hasOwnProperty('shutdown'))
        {
            newState.shutdown = state.shutdown;
        }

        newState.preUpdate = (state.hasOwnProperty('preUpdate')) ? state.preUpdate : Phaser.NOOP;
        newState.update = (state.hasOwnProperty('update')) ? state.update : Phaser.NOOP;
        newState.postUpdate = (state.hasOwnProperty('postUpdate')) ? state.postUpdate : Phaser.NOOP;
        newState.render = (state.hasOwnProperty('render')) ? state.render : Phaser.NOOP;

        return newState;
    },

    createStateFromFunction: function (key, state)
    {
        var newState = new state();

        if (newState instanceof Phaser.State)
        {
            return this.createStateFromInstance(key, newState);
        }
        else
        {
            newState.game = this.game;

            newState.settings = new Phaser.State.Settings(newState, key);

            newState.sys = new Phaser.State.Systems(newState);

            newState.sys.init();

            if (this.game.renderType === Phaser.WEBGL)
            {
                this.createStateFrameBuffer(newState);
            }

            //  Default required functions

            if (!newState.preUpdate)
            {
                newState.preUpdate = Phaser.NOOP;
            }

            if (!newState.update)
            {
                newState.update = Phaser.NOOP;
            }

            if (!newState.postUpdate)
            {
                newState.postUpdate = Phaser.NOOP;
            }

            if (!newState.render)
            {
                newState.render = Phaser.NOOP;
            }

            return newState;
        }
    },

    createStateFrameBuffer: function (newState)
    {
        var x = newState.settings.x;
        var y = newState.settings.y;

        if (newState.settings.width === -1)
        {
            newState.settings.width = this.game.width;
        }

        if (newState.settings.height === -1)
        {
            newState.settings.height = this.game.height;
        }

        var width = newState.settings.width;
        var height = newState.settings.height;

        newState.sys.fbo = this.game.renderer.createFBO(x, y, width, height);
    },

    getState: function (key)
    {
        return this.keys[key];
    },

    isActive: function (key)
    {
        var state = this.getState(key);

        return (state && state.settings.active && this.active.indexOf(state) !== -1);
    },

    start: function (key)
    {
        //  if not booted, then put state into a holding pattern
        if (!this.game.isBooted)
        {
            console.log('StateManager not yet booted, setting autoStart on pending list');

            for (var i = 0; i < this._pending.length; i++)
            {
                var entry = this._pending[i];

                if (entry.key === key)
                {
                    entry.autoStart = true;
                }
            }

            return;
        }

        var state = this.getState(key);

        if (state)
        {
            //  Already started? Nothing more to do here ...
            if (this.isActive(key))
            {
                return;
            }

            state.settings.active = true;

            //  + arguments
            if (state.init)
            {
                state.init.call(state);
            }

            if (state.preload)
            {
                state.sys.load.reset(true);

                state.preload.call(state, this.game);

                //  Is the loader empty?
                if (state.sys.load.totalQueuedFiles() === 0 && state.sys.load.totalQueuedPacks() === 0)
                {
                    // console.log('empty queue');
                    this.startCreate(state);
                }
                else
                {
                    // console.log('load start');

                    //  Start the loader going as we have something in the queue
                    // state.load.onLoadComplete.addOnce(this.loadComplete, this, 0, state);

                    state.sys.load.start();
                }
            }
            else
            {
                // console.log('no preload');

                //  No preload? Then there was nothing to load either
                this.startCreate(state);
            }

        }
    },

    loadComplete: function (state)
    {
        // console.log('loadComplete');

        //  Make sure to do load-update one last time before state is set to _created

        if (state.hasOwnProperty('loadUpdate'))
        {
            state.loadUpdate.call(state);
        }

        this.startCreate(state);
    },

    startCreate: function (state)
    {
        if (state.create)
        {
            state.create.call(state);
        }

        this.active.push(state);

        this.game.updates.running = true;
    },

    //  See if we can reduce this down to just update and render

    preUpdate: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                state.sys.children.list[c].preUpdate();
            }

            state.preUpdate();
        }
    },

    update: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            //  Invoke State Main Loop here - updating all of its systems (tweens, physics, etc)

            //  This shouldn't be called if the State is still loading
            //  Have a State.STATUS const in the Settings, dictating what is going on

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                var child = state.sys.children.list[c];

                if (child.exists)
                {
                    child.update();
                }
            }

            state.update();
        }
    },

    postUpdate: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            for (var c = 0; c < state.sys.children.list.length; c++)
            {
                state.sys.children.list[c].postUpdate();
            }

            state.postUpdate();
        }
    },

    render: function ()
    {
        for (var i = 0; i < this.active.length; i++)
        {
            var state = this.active[i];

            //  Can put all kinds of other checks in here, like MainLoop, FPS, etc.
            if (!state.settings.visible || state.sys.color.alpha === 0 || state.sys.children.list.length === 0)
            {
                continue;
            }

            this.game.renderer.render(state);
        }
    },

    renderChildren: function (renderer, state)
    {
        //  Populates the display list
        for (var c = 0; c < state.sys.children.list.length; c++)
        {
            var child = state.sys.children.list[c];

            child.render(renderer, child);
        }
    }

};
<|endoftext|>


<commit_before>import 'core-js/stable'
import 'regenerator-runtime/runtime'
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import {applyMiddleware, createStore} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension/logOnlyInProduction'
import createHistory from 'history/createBrowserHistory'
import {Redirect, Route, Router, Switch} from 'react-router-dom'
import thunkMiddleware from 'redux-thunk'
import {Map} from 'immutable'
import {save as repositorySave} from './common/LocalRepository'
import {reducer} from './reducers/Reducer'
import {filter} from './reducers/Configuration'
import {navigated} from './actions/NevergreenActionCreators'
import NevergreenContainer from './NevergreenContainer'
import MonitorContainer from './monitor/MonitorContainer'
import TrackingContainer from './tracking/TrackingContainer'
import SuccessContainer from './success/SuccessContainer'
import SettingsContainer from './settings/SettingsContainer'
import BackupContainer from './backup/BackupContainer'
import {StyleGuide} from './styleGuide/StyleGuide'
import _ from 'lodash'
import {UnhandledError} from './UnhandledError'
import {loaded} from './reducers/Selectors'
import Modal from 'react-modal'

const ONE_SECOND = 1000

const initialState = Map()
let store = createStore(reducer, initialState, composeWithDevTools(
  applyMiddleware(thunkMiddleware)
))

const save = async () => {
  const state = store.getState()
  if (loaded(state) === true) {
    await repositorySave(filter(state.toJS()))
  }
}
const saveDebounced = _.debounce(save, 200, {maxWait: ONE_SECOND})

store.subscribe(() => saveDebounced())

const history = createHistory()

history.listen(() => store.dispatch(navigated()))

Modal.setAppElement('#root')

ReactDOM.render(
  <UnhandledError>
    <Provider store={store}>
      <Router history={history}>
        <NevergreenContainer>
          <Switch>
            <Route exact path='/monitor' component={MonitorContainer}/>
            <Route exact path='/tracking' component={TrackingContainer}/>
            <Route exact path='/success' component={SuccessContainer}/>
            <Route exact path='/settings' component={SettingsContainer}/>
            <Route exact path='/backup' component={BackupContainer}/>
            <Route exact path='/style-guide' component={StyleGuide}/>
            <Route>
              <Redirect to='/tracking'/>
            </Route>
          </Switch>
        </NevergreenContainer>
      </Router>
    </Provider>
  </UnhandledError>,
  document.getElementById('root'))
<commit_msg>fixes a warning<commit_after>import 'core-js/stable'
import 'regenerator-runtime/runtime'
import React from 'react'
import ReactDOM from 'react-dom'
import {Provider} from 'react-redux'
import {applyMiddleware, createStore} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension/logOnlyInProduction'
import {createBrowserHistory} from 'history'
import {Redirect, Route, Router, Switch} from 'react-router-dom'
import thunkMiddleware from 'redux-thunk'
import {Map} from 'immutable'
import {save as repositorySave} from './common/LocalRepository'
import {reducer} from './reducers/Reducer'
import {filter} from './reducers/Configuration'
import {navigated} from './actions/NevergreenActionCreators'
import NevergreenContainer from './NevergreenContainer'
import MonitorContainer from './monitor/MonitorContainer'
import TrackingContainer from './tracking/TrackingContainer'
import SuccessContainer from './success/SuccessContainer'
import SettingsContainer from './settings/SettingsContainer'
import BackupContainer from './backup/BackupContainer'
import {StyleGuide} from './styleGuide/StyleGuide'
import _ from 'lodash'
import {UnhandledError} from './UnhandledError'
import {loaded} from './reducers/Selectors'
import Modal from 'react-modal'

const ONE_SECOND = 1000

const initialState = Map()
let store = createStore(reducer, initialState, composeWithDevTools(
  applyMiddleware(thunkMiddleware)
))

const save = async () => {
  const state = store.getState()
  if (loaded(state) === true) {
    await repositorySave(filter(state.toJS()))
  }
}
const saveDebounced = _.debounce(save, 200, {maxWait: ONE_SECOND})

store.subscribe(() => saveDebounced())

const history = createBrowserHistory()

history.listen(() => store.dispatch(navigated()))

Modal.setAppElement('#root')

ReactDOM.render(
  <UnhandledError>
    <Provider store={store}>
      <Router history={history}>
        <NevergreenContainer>
          <Switch>
            <Route exact path='/monitor' component={MonitorContainer}/>
            <Route exact path='/tracking' component={TrackingContainer}/>
            <Route exact path='/success' component={SuccessContainer}/>
            <Route exact path='/settings' component={SettingsContainer}/>
            <Route exact path='/backup' component={BackupContainer}/>
            <Route exact path='/style-guide' component={StyleGuide}/>
            <Route>
              <Redirect to='/tracking'/>
            </Route>
          </Switch>
        </NevergreenContainer>
      </Router>
    </Provider>
  </UnhandledError>,
  document.getElementById('root'))
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';

import etch from 'etch';

import {cloneRepository, buildRepository, setUpLocalAndRemoteRepositories} from '../helpers';
import StatusBarTileController from '../../lib/controllers/status-bar-tile-controller';

describe('StatusBarTileController', function() {
  let atomEnvironment, workspace, workspaceElement, commandRegistry, notificationManager;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;

    workspaceElement = atomEnvironment.views.getView(workspace);
  });

  afterEach(function() {
    atomEnvironment.destroy();
  });

  describe('branches', function() {
    it('indicates the current branch and toggles visibility of the branch menu when clicked', async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);

      const controller = new StatusBarTileController({workspace, repository, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const branchView = controller.refs.branchView;
      assert.equal(branchView.element.textContent, 'master');

      // FIXME: Remove this guard when 1.13 is on stable.
      if (parseFloat(atom.getVersion() >= 1.13)) {
        assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
        branchView.element.click();
        assert.isDefined(document.querySelectorAll('.github-BranchMenuView')[0]);
        branchView.element.click();
        assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
      }
    });

    describe('the branch menu', function() {
      describe('checking out an existing branch', function() {
        it('can check out existing branches with no conflicts', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          // create branch called 'branch'
          await repository.git.exec(['branch', 'branch']);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.deepEqual(branches, ['branch', 'master']);
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');

          list.selectedIndex = branches.indexOf('branch');
          list.onchange();
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          list.selectedIndex = branches.indexOf('master');
          list.onchange();
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');
        });

        it('displays an error message if checkout fails', async function() {
          const {localRepoPath} = await setUpLocalAndRemoteRepositories('three-files');
          const repository = await buildRepository(localRepoPath);
          await repository.git.exec(['branch', 'branch']);

          // create a conflict
          fs.writeFileSync(path.join(localRepoPath, 'a.txt'), 'a change');

          await repository.git.exec(['commit', '-a', '-m', 'change on master']);
          await repository.checkout('branch');
          fs.writeFileSync(path.join(localRepoPath, 'a.txt'), 'a change that conflicts');

          const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          sinon.stub(notificationManager, 'addError');

          list.selectedIndex = branches.indexOf('master');
          list.onchange();
          await etch.getScheduler().getNextUpdatePromise();
          assert.equal(await repository.getCurrentBranch(), 'branch');
          await assert.async.equal(list.selectedOptions[0].value, 'branch');
          await assert.async.isTrue(notificationManager.addError.called);
          const notificationArgs = notificationManager.addError.args[0];
          assert.equal(notificationArgs[0], 'Checkout aborted');
          assert.match(notificationArgs[1].description, /Local changes to the following would be overwritten/);
        });
      });

      describe('checking out newly created branches', function() {
        it('can check out newly created branches', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list, newBranchButton} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.deepEqual(branches, ['master']);
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');

          assert.isDefined(branchMenuView.refs.list);
          assert.isUndefined(branchMenuView.refs.editor);
          newBranchButton.click();
          await etch.getScheduler().getNextUpdatePromise();
          assert.isUndefined(branchMenuView.refs.list);
          assert.isDefined(branchMenuView.refs.editor);

          branchMenuView.refs.editor.setText('new-branch');
          await newBranchButton.onclick();
          repository.refresh();
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          assert.isUndefined(branchMenuView.refs.editor);
          assert.isDefined(branchMenuView.refs.list);

          assert.equal(await repository.getCurrentBranch(), 'new-branch');
          assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'new-branch');
        });

        it('displays an error message if branch already exists', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          await repository.git.exec(['checkout', '-b', 'branch']);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list, newBranchButton} = branchMenuView.refs;

          sinon.stub(notificationManager, 'addError');

          const branches = Array.from(branchMenuView.refs.list.options).map(option => option.value);
          assert.deepEqual(branches, ['branch', 'master']);
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          await newBranchButton.onclick();

          branchMenuView.refs.editor.setText('master');
          await newBranchButton.onclick();
          await assert.async.isTrue(notificationManager.addError.called);
          const notificationArgs = notificationManager.addError.args[0];
          assert.equal(notificationArgs[0], 'Cannot create branch');
          assert.match(notificationArgs[1].description, /already exists/);

          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'branch');
        });
      });
    });
  });

  describe('pushing and pulling', function() {
    it('indicates the ahead and behind counts and toggles visibility of the push pull menu when clicked', async function() {
      const {localRepoPath} = await setUpLocalAndRemoteRepositories();
      const repository = await buildRepository(localRepoPath);

      const controller = new StatusBarTileController({workspace, repository, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const pushPullView = controller.refs.pushPullView;
      const {aheadCount, behindCount} = pushPullView.refs;
      assert.equal(aheadCount.textContent, '');
      assert.equal(behindCount.textContent, '');

      await repository.git.exec(['reset', '--hard', 'head~2']);
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();
      assert.equal(aheadCount.textContent, '');
      assert.equal(behindCount.textContent, '2');

      await repository.git.commit('new local commit', {allowEmpty: true});
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();
      assert.equal(aheadCount.textContent, '1');
      assert.equal(behindCount.textContent, '2');

      // FIXME: Remove this guard when 1.13 is on stable.
      if (parseFloat(atom.getVersion() >= 1.13)) {
        assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
        pushPullView.element.click();
        assert.isDefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
        pushPullView.element.click();
        assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
      }
    });

    describe('the push/pull menu', function() {
      it('disables the fetch and pull buttons when there is no remote tracking branch and displays informative message', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);
        await repository.git.exec(['checkout', '-b', 'new-branch']);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        const pushPullMenuView = controller.pushPullMenuView;
        const {pushButton, pullButton, fetchButton, message} = pushPullMenuView.refs;

        assert.isTrue(pullButton.disabled);
        assert.isTrue(fetchButton.disabled);
        assert.match(message.innerHTML, /No remote detected.*Pushing will set up a remote tracking branch/);

        pushButton.dispatchEvent(new MouseEvent('click'));
        repository.refresh();
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        assert.isFalse(pullButton.disabled);
        assert.isFalse(fetchButton.disabled);
        assert.equal(message.textContent, '');
      });

      it('displays an error message if push fails and allows force pushing if meta key is pressed', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);
        await repository.git.exec(['reset', '--hard', 'head~2']);
        await repository.git.commit('another commit', {allowEmpty: true});

        const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        const pushPullMenuView = controller.pushPullMenuView;
        const {pushButton, pullButton} = pushPullMenuView.refs;

        sinon.stub(notificationManager, 'addError');

        assert.equal(pushButton.textContent, 'Push (1)');
        assert.equal(pullButton.textContent, 'Pull (2)');

        pushButton.dispatchEvent(new MouseEvent('click'));
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        await assert.async.isTrue(notificationManager.addError.called);
        const notificationArgs = notificationManager.addError.args[0];
        assert.equal(notificationArgs[0], 'Push rejected');
        assert.match(notificationArgs[1].description, /Try pulling before pushing again/);

        pushButton.dispatchEvent(new MouseEvent('click', {metaKey: true}));
        repository.refresh();
        await controller.getLastModelDataRefreshPromise();

        await assert.async.equal(pushButton.textContent, 'Push ');
        await assert.async.equal(pullButton.textContent, 'Pull ');
      });
    });

    describe('fetch and pull commands', function() {
      it('fetches when github:fetch is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'fetch');

        commandRegistry.dispatch(workspaceElement, 'github:fetch');

        assert.isTrue(repository.fetch.called);
      });

      it('pulls when github:pull is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'pull');

        commandRegistry.dispatch(workspaceElement, 'github:pull');

        assert.isTrue(repository.pull.called);
      });

      it('pushes when github:push is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'push');

        commandRegistry.dispatch(workspaceElement, 'github:push');

        assert.isTrue(repository.push.calledWith('master', sinon.match({force: false, setUpstream: false})));
      });

      it('force pushes when github:force-push is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'push');

        commandRegistry.dispatch(workspaceElement, 'github:force-push');

        assert.isTrue(repository.push.calledWith('master', sinon.match({force: true, setUpstream: false})));
      });
    });
  });

  describe('changed files', function() {
    it('shows the changed files count view when the repository data is loaded', async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);

      const toggleGitPanel = sinon.spy();
      const controller = new StatusBarTileController({workspace, repository, toggleGitPanel, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const changedFilesCountView = controller.refs.changedFilesCountView;

      assert.equal(changedFilesCountView.element.textContent, '0 files');

      fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
      fs.unlinkSync(path.join(workdirPath, 'b.txt'));
      repository.refresh();
      await repository.stageFiles(['a.txt']);
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      assert.equal(changedFilesCountView.element.textContent, '2 files');

      changedFilesCountView.element.click();
      assert(toggleGitPanel.calledOnce);
    });
  });
});
<commit_msg>Flakes!<commit_after>import fs from 'fs';
import path from 'path';

import etch from 'etch';
import until from 'test-until';

import {cloneRepository, buildRepository, setUpLocalAndRemoteRepositories} from '../helpers';
import StatusBarTileController from '../../lib/controllers/status-bar-tile-controller';

describe('StatusBarTileController', function() {
  let atomEnvironment, workspace, workspaceElement, commandRegistry, notificationManager;

  beforeEach(function() {
    atomEnvironment = global.buildAtomEnvironment();
    workspace = atomEnvironment.workspace;
    commandRegistry = atomEnvironment.commands;
    notificationManager = atomEnvironment.notifications;

    workspaceElement = atomEnvironment.views.getView(workspace);
  });

  afterEach(function() {
    atomEnvironment.destroy();
  });

  describe('branches', function() {
    it('indicates the current branch and toggles visibility of the branch menu when clicked', async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);

      const controller = new StatusBarTileController({workspace, repository, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const branchView = controller.refs.branchView;
      assert.equal(branchView.element.textContent, 'master');

      // FIXME: Remove this guard when 1.13 is on stable.
      if (parseFloat(atom.getVersion() >= 1.13)) {
        assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
        branchView.element.click();
        assert.isDefined(document.querySelectorAll('.github-BranchMenuView')[0]);
        branchView.element.click();
        assert.isUndefined(document.querySelectorAll('.github-BranchMenuView')[0]);
      }
    });

    describe('the branch menu', function() {
      describe('checking out an existing branch', function() {
        it('can check out existing branches with no conflicts', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          // create branch called 'branch'
          await repository.git.exec(['branch', 'branch']);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.deepEqual(branches, ['branch', 'master']);
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');

          list.selectedIndex = branches.indexOf('branch');
          list.onchange();
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          list.selectedIndex = branches.indexOf('master');
          list.onchange();
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');
        });

        it('displays an error message if checkout fails', async function() {
          const {localRepoPath} = await setUpLocalAndRemoteRepositories('three-files');
          const repository = await buildRepository(localRepoPath);
          await repository.git.exec(['branch', 'branch']);

          // create a conflict
          fs.writeFileSync(path.join(localRepoPath, 'a.txt'), 'a change');

          await repository.git.exec(['commit', '-a', '-m', 'change on master']);
          await repository.checkout('branch');
          fs.writeFileSync(path.join(localRepoPath, 'a.txt'), 'a change that conflicts');

          const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          sinon.stub(notificationManager, 'addError');

          list.selectedIndex = branches.indexOf('master');
          list.onchange();
          await etch.getScheduler().getNextUpdatePromise();
          assert.equal(await repository.getCurrentBranch(), 'branch');
          await assert.async.equal(list.selectedOptions[0].value, 'branch');
          await assert.async.isTrue(notificationManager.addError.called);
          const notificationArgs = notificationManager.addError.args[0];
          assert.equal(notificationArgs[0], 'Checkout aborted');
          assert.match(notificationArgs[1].description, /Local changes to the following would be overwritten/);
        });
      });

      describe('checking out newly created branches', function() {
        it('can check out newly created branches', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list, newBranchButton} = branchMenuView.refs;

          const branches = Array.from(list.options).map(option => option.value);
          assert.deepEqual(branches, ['master']);
          assert.equal(await repository.getCurrentBranch(), 'master');
          assert.equal(list.selectedOptions[0].value, 'master');

          assert.isDefined(branchMenuView.refs.list);
          assert.isUndefined(branchMenuView.refs.editor);
          newBranchButton.click();
          await etch.getScheduler().getNextUpdatePromise();
          assert.isUndefined(branchMenuView.refs.list);
          assert.isDefined(branchMenuView.refs.editor);

          branchMenuView.refs.editor.setText('new-branch');
          await newBranchButton.onclick();
          repository.refresh();
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          assert.isUndefined(branchMenuView.refs.editor);
          assert.isDefined(branchMenuView.refs.list);

          assert.equal(await repository.getCurrentBranch(), 'new-branch');
          assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'new-branch');
        });

        it('displays an error message if branch already exists', async function() {
          const workdirPath = await cloneRepository('three-files');
          const repository = await buildRepository(workdirPath);

          await repository.git.exec(['checkout', '-b', 'branch']);

          const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
          await controller.getLastModelDataRefreshPromise();
          await etch.getScheduler().getNextUpdatePromise();

          const branchMenuView = controller.branchMenuView;
          const {list, newBranchButton} = branchMenuView.refs;

          sinon.stub(notificationManager, 'addError');

          const branches = Array.from(branchMenuView.refs.list.options).map(option => option.value);
          assert.deepEqual(branches, ['branch', 'master']);
          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(list.selectedOptions[0].value, 'branch');

          await newBranchButton.onclick();

          branchMenuView.refs.editor.setText('master');
          await newBranchButton.onclick();
          await assert.async.isTrue(notificationManager.addError.called);
          const notificationArgs = notificationManager.addError.args[0];
          assert.equal(notificationArgs[0], 'Cannot create branch');
          assert.match(notificationArgs[1].description, /already exists/);

          assert.equal(await repository.getCurrentBranch(), 'branch');
          assert.equal(branchMenuView.refs.list.selectedOptions[0].value, 'branch');
        });
      });
    });
  });

  describe('pushing and pulling', function() {
    it('indicates the ahead and behind counts and toggles visibility of the push pull menu when clicked', async function() {
      const {localRepoPath} = await setUpLocalAndRemoteRepositories();
      const repository = await buildRepository(localRepoPath);

      const controller = new StatusBarTileController({workspace, repository, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const pushPullView = controller.refs.pushPullView;
      const {aheadCount, behindCount} = pushPullView.refs;
      assert.equal(aheadCount.textContent, '');
      assert.equal(behindCount.textContent, '');

      await repository.git.exec(['reset', '--hard', 'head~2']);
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();
      assert.equal(aheadCount.textContent, '');
      assert.equal(behindCount.textContent, '2');

      await repository.git.commit('new local commit', {allowEmpty: true});
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();
      assert.equal(aheadCount.textContent, '1');
      assert.equal(behindCount.textContent, '2');

      // FIXME: Remove this guard when 1.13 is on stable.
      if (parseFloat(atom.getVersion() >= 1.13)) {
        assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
        pushPullView.element.click();
        assert.isDefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
        pushPullView.element.click();
        assert.isUndefined(document.querySelectorAll('.github-PushPullMenuView')[0]);
      }
    });

    describe('the push/pull menu', function() {
      it('disables the fetch and pull buttons when there is no remote tracking branch and displays informative message', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);
        await repository.git.exec(['checkout', '-b', 'new-branch']);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        const pushPullMenuView = controller.pushPullMenuView;
        const {pushButton, pullButton, fetchButton, message} = pushPullMenuView.refs;

        assert.isTrue(pullButton.disabled);
        assert.isTrue(fetchButton.disabled);
        assert.match(message.innerHTML, /No remote detected.*Pushing will set up a remote tracking branch/);

        pushButton.dispatchEvent(new MouseEvent('click'));
        await until(async fail => {
          try {
            repository.refresh();
            await controller.getLastModelDataRefreshPromise();
            await etch.getScheduler().getNextUpdatePromise();

            assert.isFalse(pullButton.disabled);
            assert.isFalse(fetchButton.disabled);
            assert.equal(message.textContent, '');
            return true;
          } catch (err) {
            return fail(err);
          }
        });
      });

      it('displays an error message if push fails and allows force pushing if meta key is pressed', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);
        await repository.git.exec(['reset', '--hard', 'head~2']);
        await repository.git.commit('another commit', {allowEmpty: true});

        const controller = new StatusBarTileController({workspace, repository, commandRegistry, notificationManager});
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        const pushPullMenuView = controller.pushPullMenuView;
        const {pushButton, pullButton} = pushPullMenuView.refs;

        sinon.stub(notificationManager, 'addError');

        assert.equal(pushButton.textContent, 'Push (1)');
        assert.equal(pullButton.textContent, 'Pull (2)');

        pushButton.dispatchEvent(new MouseEvent('click'));
        await controller.getLastModelDataRefreshPromise();
        await etch.getScheduler().getNextUpdatePromise();

        await assert.async.isTrue(notificationManager.addError.called);
        const notificationArgs = notificationManager.addError.args[0];
        assert.equal(notificationArgs[0], 'Push rejected');
        assert.match(notificationArgs[1].description, /Try pulling before pushing again/);

        pushButton.dispatchEvent(new MouseEvent('click', {metaKey: true}));
        repository.refresh();
        await controller.getLastModelDataRefreshPromise();

        await assert.async.equal(pushButton.textContent, 'Push ');
        await assert.async.equal(pullButton.textContent, 'Pull ');
      });
    });

    describe('fetch and pull commands', function() {
      it('fetches when github:fetch is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'fetch');

        commandRegistry.dispatch(workspaceElement, 'github:fetch');

        assert.isTrue(repository.fetch.called);
      });

      it('pulls when github:pull is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories('multiple-commits', {remoteAhead: true});
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'pull');

        commandRegistry.dispatch(workspaceElement, 'github:pull');

        assert.isTrue(repository.pull.called);
      });

      it('pushes when github:push is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'push');

        commandRegistry.dispatch(workspaceElement, 'github:push');

        assert.isTrue(repository.push.calledWith('master', sinon.match({force: false, setUpstream: false})));
      });

      it('force pushes when github:force-push is triggered', async function() {
        const {localRepoPath} = await setUpLocalAndRemoteRepositories();
        const repository = await buildRepository(localRepoPath);

        const controller = new StatusBarTileController({workspace, repository, commandRegistry});
        await controller.getLastModelDataRefreshPromise();

        sinon.spy(repository, 'push');

        commandRegistry.dispatch(workspaceElement, 'github:force-push');

        assert.isTrue(repository.push.calledWith('master', sinon.match({force: true, setUpstream: false})));
      });
    });
  });

  describe('changed files', function() {
    it('shows the changed files count view when the repository data is loaded', async function() {
      const workdirPath = await cloneRepository('three-files');
      const repository = await buildRepository(workdirPath);

      const toggleGitPanel = sinon.spy();
      const controller = new StatusBarTileController({workspace, repository, toggleGitPanel, commandRegistry});
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      const changedFilesCountView = controller.refs.changedFilesCountView;

      assert.equal(changedFilesCountView.element.textContent, '0 files');

      fs.writeFileSync(path.join(workdirPath, 'a.txt'), 'a change\n');
      fs.unlinkSync(path.join(workdirPath, 'b.txt'));
      repository.refresh();
      await repository.stageFiles(['a.txt']);
      repository.refresh();
      await controller.getLastModelDataRefreshPromise();
      await etch.getScheduler().getNextUpdatePromise();

      assert.equal(changedFilesCountView.element.textContent, '2 files');

      changedFilesCountView.element.click();
      assert(toggleGitPanel.calledOnce);
    });
  });
});
<|endoftext|>


<commit_before>/*
 * Calaca - Search UI for Elasticsearch
 * https://github.com/romansanchez/Calaca
 * http://romansanchez.me
 * @rooomansanchez
 *
 * v1.2.0
 * MIT License
 */

/* Configs */
/**
 *
 * url - Cluster http url
 * index_name - Index name or comma-separated list
 * type - Type
 * size - Number of results to display at a time when pagination is enabled.
 * search_delay - Delay between actual search request in ms. Reduces number of queries to cluster by not making a request on each keystroke.
 */

var CALACA_CONFIGS = {
	url: "",
	index_name: "",
	type: "",
	size: 10,
	search_delay: 500
}<commit_msg>point to dachstein<commit_after>/*
 * Calaca - Search UI for Elasticsearch
 * https://github.com/romansanchez/Calaca
 * http://romansanchez.me
 * @rooomansanchez
 *
 * v1.2.0
 * MIT License
 */

/* Configs */
/**
 *
 * url - Cluster http url
 * index_name - Index name or comma-separated list
 * type - Type
 * size - Number of results to display at a time when pagination is enabled.
 * search_delay - Delay between actual search request in ms. Reduces number of queries to cluster by not making a request on each keystroke.
 */

var CALACA_CONFIGS = {
	url: "http://dachstein.biochem.mpg.de:9200/",
	index_name: "sample",
	type: "",
	size: 10,
	search_delay: 500
}<|endoftext|>


<commit_before>Lettris.Game = function (game) {
};

Lettris.Game.prototype = {
    create: function (game) {

	// Physics stuff
	game.physics.startSystem(Phaser.Physics.P2JS);
	game.physics.p2.setBoundsToWorld(true, true, false, true)
	game.physics.p2.gravity.y = 300;
	game.physics.p2.restitution = 0.05

	game.world.setBounds(0, 0, game.width, game.height-80);

	this.gameData = {score: 0, karma: 0}

	this.gui = new GUI(game, this.gameData)
	this.bag = new Bag(game,
			   'let-eng-std',
			   this.gameData,
			   this.gui.boxClicked)

	this.boxes = game.add.group();

	// Start box-droping loop
	game.time.events.loop(Phaser.Timer.SECOND * 2,
			      this.spawn_box, this)
    },

    spawn_box: function () {
	// check if any box is stuck above screen => game over
	this.boxes.forEach(function(box) {
	    if( box.y < 0)
		this.state.start('GameOver', true, false, this.gui.score);
	}, this);

	// put out a new box
	var widthHalf = this.game.cache.getImage('box').width / 2;
	var pos = this.game.rnd.integerInRange(
	    widthHalf + 1, this.game.width - widthHalf - 1);

	var box = this.bag.dropBox(pos)
	console.log(this.gameData.karma)

	this.boxes.add(box)
    },
};
<commit_msg>Fix gameover score bug<commit_after>Lettris.Game = function (game) {
};

Lettris.Game.prototype = {
    create: function (game) {

	// Physics stuff
	game.physics.startSystem(Phaser.Physics.P2JS);
	game.physics.p2.setBoundsToWorld(true, true, false, true)
	game.physics.p2.gravity.y = 300;
	game.physics.p2.restitution = 0.05

	game.world.setBounds(0, 0, game.width, game.height-80);

	this.gameData = {score: 0, karma: 0}

	this.gui = new GUI(game, this.gameData)
	this.bag = new Bag(game,
			   'let-eng-std',
			   this.gameData,
			   this.gui.boxClicked)

	this.boxes = game.add.group();

	// Start box-droping loop
	game.time.events.loop(Phaser.Timer.SECOND * 2,
			      this.spawn_box, this)
    },

    spawn_box: function () {
	// check if any box is stuck above screen => game over
	this.boxes.forEach(function(box) {
	    if( box.y < 0)
		this.state.start('GameOver', true, false, this.gameData.score);
	}, this);

	// put out a new box
	var widthHalf = this.game.cache.getImage('box').width / 2;
	var pos = this.game.rnd.integerInRange(
	    widthHalf + 1, this.game.width - widthHalf - 1);

	var box = this.bag.dropBox(pos)
	console.log(this.gameData.karma)

	this.boxes.add(box)
    },
};
<|endoftext|>


<commit_before>dojo.provide("dojox.fx._base");
dojo.experimental("dojox.fx");

dojo.require("dojo.fx");

// convenience functions/maps
// so you can dojox.fx[animationMethod](args) without needing to accomodate
// for the dojo.fx animation cases.
dojox.fx.chain = dojo.fx.chain;
dojox.fx.combine = dojo.fx.combine;
dojox.fx.slideIn = dojo.fx.slideIn;
dojox.fx.slideOut = dojo.fx.slideOut;
dojox.fx.slideTo = dojo.fx.slideTo;

/* dojox.fx _Animations: */
dojox.fx.sizeTo = function(/* Object */args){
	// summary:
	//		Returns an animation that will size "node"
	//		defined in args Object about it's center to
	//		a width and height defined by (args.width, args.height),
	//		supporting an optional method: chain||combine mixin
	//		(defaults to chain).
	//
	//		- works best on absolutely or relatively positioned
	//		elements?
	//
	// example:
	//
	//	dojo.fx.sizeTo({ node:'myNode',
	//		duration: 1000,
	//		width: 400,
	//		height: 200,
	//		method: "chain"
	//	}).play();
	//
	//
	var node = args.node = dojo.byId(args.node);
	var compute = dojo.getComputedStyle;

	var method = args.method || "chain";
	if (method=="chain"){ args.duration = (args.duration/2); }

	var top, newTop, left, newLeft, width, height = null;

	var init = (function(){
		var innerNode = node;
		return function(){
			var pos = compute(innerNode).position;
			top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node).top) || 0);
			left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node).left) || 0);
			width = parseInt(dojo.style(node,'width'));
			height = parseInt(dojo.style(node,'height'));

			newLeft = left - ((args.width - width)/2);
			newTop = top - ((args.height - height)/2);

			if(pos != 'absolute' && pos != 'relative'){
				var ret = dojo.coords(innerNode, true);
				top = ret.y;
				left = ret.x;
				innerNode.style.position="absolute";
				innerNode.style.top=top+"px";
				innerNode.style.left=left+"px";
			}
		}
	})();
	init(); // hmmm, do we need to init() or just the once beforeBegin?

	var anim1 = dojo.animateProperty(dojo.mixin({
		properties: {
			height: { start: height, end: args.height || 0, unit:"px" },
			top: { start: top, end: newTop }
		}
	}, args));
	var anim2 = dojo.animateProperty(dojo.mixin({
		properties: {
			width: { start: width, end: args.width || 0, unit:"px" },
			left: { start: left, end: newLeft }
		}
	}, args));

	// FIXME:
	// dojo.fx[args.method]([anim1,anim2]);
	if(args.method == "combine"){
		var anim = dojo.fx.combine([anim1,anim2]);
	}else{
		var anim = dojo.fx.chain([anim1,anim2]);
	}
	dojo.connect(anim, "beforeBegin", anim, init);
	return anim; // dojo._Animation
};

/* dojox.fx CSS Class _Animations: */

dojox.fx.addClass = function(/* Object */args){
	// summary:
	//		returns an animation that will animate
	//		the properieds of a node to the properties
	//		defined in a standard CSS .class definition.
	//		(calculating the differences itself)
	//
	//		standard _Animation object rules apply.
	//
	// additonal mixins:
	//
	//		args.cssClass: String - class string (to be added onEnd)
	//
	var node = args.node = dojo.byId(args.node);

	var pushClass = (function(){
		// summary: onEnd we want to add the class to the node
		//	(as dojo.addClass naturally would) in case our
		//	class parsing misses anything the browser would
		// 	otherwise interpret. this may cause some flicker,
		//	and will only apply the class so children can inherit
		//	after the animation is done (potentially more flicker)
		var innerNode = node; // FIXME: why do we do this like this?
		return function(){
			dojo.addClass(innerNode, args.cssClass);
			innerNode.style.cssText = _beforeStyle;
		}
	})();

	// _getCalculatedStleChanges is the core of our style/class animations
	var mixedProperties = dojox.fx._getCalculatedStyleChanges(args,true);
	var _beforeStyle = node.style.cssText;
	var _anim = dojo.animateProperty(dojo.mixin({
		properties: mixedProperties
	},args));
	dojo.connect(_anim,"onEnd",_anim,pushClass);
	return _anim;

};

dojox.fx.removeClass = function(/* Object */args){
	// summary:
	//	returns an animation that will animate the properieds of a
	// 	node (args.node) to the properties calculated after removing
	//	a standard CSS className from a that node.
	//
	//	calls dojo.removeClass(args.cssClass) onEnd of animation
	//
	//	standard dojo._Animation object rules apply.
	//
	// additonal mixins:
	//
	//	args.cssClass: String - class string (to be removed from node)
	//
	var node = args.node = dojo.byId(args.node);

	var pullClass = (function(){
		// summary: onEnd we want to remove the class from the node
		//	(as dojo.removeClass naturally would) in case our class
		//	parsing misses anything the browser would otherwise
		//	interpret. this may cause some flicker, and will only
		//	apply the class so children can inherit after the
		//	animation is done (potentially more flicker)
		//
		var innerNode = node;
		return function(){
			dojo.removeClass(innerNode, args.cssClass);
			innerNode.style.cssText = _beforeStyle;
		}
	})();

	var mixedProperties = dojox.fx._getCalculatedStyleChanges(args,false);
	var _beforeStyle = node.style.cssText;
	var _anim = dojo.animateProperty(dojo.mixin({
		properties: mixedProperties
	},args));
	dojo.connect(_anim,"onEnd",_anim,pullClass);
	return _anim;
};

dojox.fx._allowedProperties = [
	// summary:
	//	this is our pseudo map of properties we will check for.
	//	it should be much more intuitive. a way to normalize and
	//	"predict" intent, or even something more clever ...
	//	open to suggestions.

	"width",
	"height",

	// these need to be filtered through dojo.colors?
	// "background", // normalize to:
		/* "backgroundImage", */
		"backgroundColor", // so we can use background offset?
	"color",

	//
	// "border", // the normalize on this one will be _hideous_
	//	(color/style/width)
	//	(left,top,right,bottom for each of _those_)
	//

	// only if pos = absolute || relative?
	"left", "top", "right", "bottom",

	"padding", // normalize to:
	"paddingLeft", "paddingRight", "paddingTop", "paddingBottom",

	"margin", // normalize to:
	"marginLeft", "marginTop", "marginRight", "marginBottom",

	// unit import/delicate?:
	"lineHeight",
	"letterSpacing",
	"fontSize"
];

dojox.fx._getStyleSnapshot = function(/* Object */cache){
	// summary:
	//	uses a dojo.getComputedStyle(node) cache reference and
	// 	iterates through the 'documented/supported animate-able'
	// 	properties.
	//
	// returns:  Array
	//	an array of raw, calculcated values, to be normalized/compared
	//	elsewhere

	return dojo.map(dojox.fx._allowedProperties,function(style){
		return cache[style]; // String
	}); // Array
};

dojox.fx._getCalculatedStyleChanges = function(/* Object */args, /*Boolean*/addClass){
	// summary:
	//	calculate and normalize(?) the differences between two states
	//	of a node (args.node) by either quickly adding or removing
	//	a class (and if that causes poor flicker later, we can attempt
	//	to create a cloned node offscreen and do other weird calculations
	//
	// args:
	// 	we are expecting args.node (DomNode) and
	//	args.cssClass (class String)
	//
	// addClass:
	// 	true to calculate what adding a class would do,
	// 	false to calculate what removing the class would do

	var node = args.node = dojo.byId(args.node);
	var compute = dojo.getComputedStyle(node);

	// take our snapShots
	var _before = dojox.fx._getStyleSnapshot(compute);
	dojo[(addClass ? "addClass" : "removeClass")](node,args.cssClass);
	var _after = dojox.fx._getStyleSnapshot(compute);
	dojo[(addClass ? "removeClass" : "addClass")](node,args.cssClass);

	var calculated = {};
	var i = 0;
	dojo.forEach(dojox.fx._allowedProperties,function(prop){
		if(_before[i] != _after[i]){
			// FIXME: the static unit: px is not good, either. need to parse unit from computed style?
			calculated[prop] = { end: parseInt(_after[i]), unit: 'px' };
		}
		i++;
	});
	return calculated;
};
<commit_msg>Rename slideIn/slideOut to wipeIn/wipeOut.  Fixes #3917<commit_after>dojo.provide("dojox.fx._base");
dojo.experimental("dojox.fx");

dojo.require("dojo.fx");

// convenience functions/maps
// so you can dojox.fx[animationMethod](args) without needing to accomodate
// for the dojo.fx animation cases.
dojox.fx.chain = dojo.fx.chain;
dojox.fx.combine = dojo.fx.combine;
dojox.fx.wipeIn = dojo.fx.wipeIn;
dojox.fx.wipeOut = dojo.fx.wipeOut;
dojox.fx.slideTo = dojo.fx.slideTo;

/* dojox.fx _Animations: */
dojox.fx.sizeTo = function(/* Object */args){
	// summary:
	//		Returns an animation that will size "node"
	//		defined in args Object about it's center to
	//		a width and height defined by (args.width, args.height),
	//		supporting an optional method: chain||combine mixin
	//		(defaults to chain).
	//
	//		- works best on absolutely or relatively positioned
	//		elements?
	//
	// example:
	//
	//	dojo.fx.sizeTo({ node:'myNode',
	//		duration: 1000,
	//		width: 400,
	//		height: 200,
	//		method: "chain"
	//	}).play();
	//
	//
	var node = (args.node = dojo.byId(args.node));
	var compute = dojo.getComputedStyle;

	var method = args.method || "chain";
	if (method=="chain"){ args.duration = (args.duration/2); }

	var top, newTop, left, newLeft, width, height = null;

	var init = (function(){
		var innerNode = node;
		return function(){
			var pos = compute(innerNode).position;
			top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node).top) || 0);
			left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node).left) || 0);
			width = parseInt(dojo.style(node,'width'));
			height = parseInt(dojo.style(node,'height'));

			newLeft = left - ((args.width - width)/2);
			newTop = top - ((args.height - height)/2);

			if(pos != 'absolute' && pos != 'relative'){
				var ret = dojo.coords(innerNode, true);
				top = ret.y;
				left = ret.x;
				innerNode.style.position="absolute";
				innerNode.style.top=top+"px";
				innerNode.style.left=left+"px";
			}
		}
	})();
	init(); // hmmm, do we need to init() or just the once beforeBegin?

	var anim1 = dojo.animateProperty(dojo.mixin({
		properties: {
			height: { start: height, end: args.height || 0, unit:"px" },
			top: { start: top, end: newTop }
		}
	}, args));
	var anim2 = dojo.animateProperty(dojo.mixin({
		properties: {
			width: { start: width, end: args.width || 0, unit:"px" },
			left: { start: left, end: newLeft }
		}
	}, args));

	// FIXME:
	// dojo.fx[args.method]([anim1,anim2]);
	var anim = dojo.fx[((args.method == "combine") ? "combine" : "chain")]([anim1,anim2]);
	dojo.connect(anim, "beforeBegin", anim, init);
	return anim; // dojo._Animation
};

/* dojox.fx CSS Class _Animations: */

dojox.fx.addClass = function(/* Object */args){
	// summary:
	//		returns an animation that will animate
	//		the properieds of a node to the properties
	//		defined in a standard CSS .class definition.
	//		(calculating the differences itself)
	//
	//		standard _Animation object rules apply.
	//
	// additonal mixins:
	//
	//		args.cssClass: String - class string (to be added onEnd)
	//
	var node = (args.node = dojo.byId(args.node));

	var pushClass = (function(){
		// summary: onEnd we want to add the class to the node
		//	(as dojo.addClass naturally would) in case our
		//	class parsing misses anything the browser would
		// 	otherwise interpret. this may cause some flicker,
		//	and will only apply the class so children can inherit
		//	after the animation is done (potentially more flicker)
		var innerNode = node; // FIXME: why do we do this like this?
		return function(){
			dojo.addClass(innerNode, args.cssClass);
			innerNode.style.cssText = _beforeStyle;
		}
	})();

	// _getCalculatedStleChanges is the core of our style/class animations
	var mixedProperties = dojox.fx._getCalculatedStyleChanges(args,true);
	var _beforeStyle = node.style.cssText;
	var _anim = dojo.animateProperty(dojo.mixin({
		properties: mixedProperties
	},args));
	dojo.connect(_anim,"onEnd",_anim,pushClass);
	return _anim;

};

dojox.fx.removeClass = function(/* Object */args){
	// summary:
	//	returns an animation that will animate the properieds of a
	// 	node (args.node) to the properties calculated after removing
	//	a standard CSS className from a that node.
	//
	//	calls dojo.removeClass(args.cssClass) onEnd of animation
	//
	//	standard dojo._Animation object rules apply.
	//
	// additonal mixins:
	//
	//	args.cssClass: String - class string (to be removed from node)
	//
	var node = (args.node = dojo.byId(args.node));

	var pullClass = (function(){
		// summary: onEnd we want to remove the class from the node
		//	(as dojo.removeClass naturally would) in case our class
		//	parsing misses anything the browser would otherwise
		//	interpret. this may cause some flicker, and will only
		//	apply the class so children can inherit after the
		//	animation is done (potentially more flicker)
		//
		var innerNode = node;
		return function(){
			dojo.removeClass(innerNode, args.cssClass);
			innerNode.style.cssText = _beforeStyle;
		}
	})();

	var mixedProperties = dojox.fx._getCalculatedStyleChanges(args,false);
	var _beforeStyle = node.style.cssText;
	var _anim = dojo.animateProperty(dojo.mixin({
		properties: mixedProperties
	},args));
	dojo.connect(_anim,"onEnd",_anim,pullClass);
	return _anim;
};

dojox.fx._allowedProperties = [
	// summary:
	//	this is our pseudo map of properties we will check for.
	//	it should be much more intuitive. a way to normalize and
	//	"predict" intent, or even something more clever ...
	//	open to suggestions.

	"width",
	"height",

	// these need to be filtered through dojo.colors?
	// "background", // normalize to:
		/* "backgroundImage", */
		"backgroundColor", // so we can use background offset?
	"color",

	//
	// "border", // the normalize on this one will be _hideous_
	//	(color/style/width)
	//	(left,top,right,bottom for each of _those_)
	//

	// only if pos = absolute || relative?
	"left", "top", "right", "bottom",

	"padding", // normalize to:
	"paddingLeft", "paddingRight", "paddingTop", "paddingBottom",

	"margin", // normalize to:
	"marginLeft", "marginTop", "marginRight", "marginBottom",

	// unit import/delicate?:
	"lineHeight",
	"letterSpacing",
	"fontSize"
];

dojox.fx._getStyleSnapshot = function(/* Object */cache){
	// summary:
	//	uses a dojo.getComputedStyle(node) cache reference and
	// 	iterates through the 'documented/supported animate-able'
	// 	properties.
	//
	// returns:  Array
	//	an array of raw, calculcated values, to be normalized/compared
	//	elsewhere

	return dojo.map(dojox.fx._allowedProperties,function(style){
		return cache[style]; // String
	}); // Array
};

dojox.fx._getCalculatedStyleChanges = function(/* Object */args, /*Boolean*/addClass){
	// summary:
	//	calculate and normalize(?) the differences between two states
	//	of a node (args.node) by either quickly adding or removing
	//	a class (and if that causes poor flicker later, we can attempt
	//	to create a cloned node offscreen and do other weird calculations
	//
	// args:
	// 	we are expecting args.node (DomNode) and
	//	args.cssClass (class String)
	//
	// addClass:
	// 	true to calculate what adding a class would do,
	// 	false to calculate what removing the class would do

	var node = (args.node = dojo.byId(args.node));
	var compute = dojo.getComputedStyle(node);

	// take our snapShots
	var _before = dojox.fx._getStyleSnapshot(compute);
	dojo[(addClass ? "addClass" : "removeClass")](node,args.cssClass);
	var _after = dojox.fx._getStyleSnapshot(compute);
	dojo[(addClass ? "removeClass" : "addClass")](node,args.cssClass);

	var calculated = {};
	var i = 0;
	dojo.forEach(dojox.fx._allowedProperties,function(prop){
		if(_before[i] != _after[i]){
			// FIXME: the static unit: px is not good, either. need to parse unit from computed style?
			calculated[prop] = { end: parseInt(_after[i]), unit: 'px' };
		}
		i++;
	});
	return calculated;
};
<|endoftext|>