<commit_before>/* ************************************************************************

    License: MIT Licence

    Description:
        Simple synchronizer/chainer for callback list of functions
        synchronizer : Load many functions at same time, when they all finish
                       raise the final callback
        chainer : Load many functions one by one, when last one finish raise
                  the final callback

************************************************************************ */

//Simple synchronizer/chainer for Array of functions
a.callback = {};

/**
 * Load many functions at same time,
 * when they all finish raise the final callback
 *
 * @class synchronizer
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.synchronizer = function(callbacks, success, error) {
    return a.extend(
            new a.callback.synchronizerInstance(
                callbacks,
                success,
                error
            ),
            new a.eventEmitter('a.callback.synchronizer')
        );
};

/**
 * synchronizerInstance, NEVER use like this,
 * use a.callback.synchronizer instead.
 *
 * @class synchronizerInstance
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.synchronizerInstance = function(callbacks, success, error) {
    this.callbacks       = callbacks || [];
    this.successFunction = success;
    this.errorFunction   = error;
    this.data            = {};
    this.resultScope     = null;
    this.scope           = null;
    this.parrallelCount  = 0;
    this.running         = false;
};

a.callback.synchronizerInstance.prototype = {
    /**
     * Add callback to existing callback list.
     * If the system is started, also append this callback to waiting queue.
     *
     * @method addCallback
     *
     * @param {Array}                       Any number of functions to chain
     *                                      The first function will be executed
     *                                      at first, and the last at last, in
     *                                      the order you give to that fct.
    */
    addCallback: function() {
        var args = a.toArray(arguments);

        this.callbacks = this.callbacks.concat(args);

        if(this.isRunning()) {
            var scope = this.scope || this,
                result = this.getResultObject();

            a.each(args, function(callback) {
                callback.call(scope, result);
            });
        }
    },

    /**
     * Remove callback from existing callback list.
     *
     * @method removeCallback
     *
     * @param fct {Function}                The function to remove from list
    */
    removeCallback: function(fct) {
        this.callbacks = a.without(this.callbacks, fct);
    },

    /**
     * Apply this scope to all callback function
     *
     * @method setScope
     *
     * @param scope {Object}                The scope to apply to callbacks
    */
    setScope: function(scope) {
        if(a.isTrueObject(scope)) {
            this.scope = scope;
        }
    },

    /**
     * Get a currently stored data.
     *
     * @method getData
     *
     * @param key {String}                  The key linked to value to get data
     * @return {Object | null}              The value previously stored and
     *                                      content
    */
    getData: function(key) {
        return this.data[key] || null;
    },

    /**
     * Set a new data stored into container
     *
     * @method setData
     *
     * @param key {String}                  The key to retrieve value later
     * @param value {Object}                Any value to store, a null or
     *                                      undefined element will erase key
     *                                      from store
    */
    setData: function(key, value) {
        if(a.isNone(value)) {
            delete this.data[key];
        } else {
            this.data[key] = value;
        }
    },

    /**
     * Get the main callback object to manipulate chain from it.
     *
     * @method getResultObject
     *
     * @return {Object}                     An object ready to use for
     *                                      controlling chain process
    */
    getResultObject: function() {
        var n = a.scope(this.next, this),
            s = a.scope(this.stop, this);
        return {
            next: n, done: n, success: n,
            fail: s, error: s, stop: s,
            setData: a.scope(this.setData, this),
            getData: a.scope(this.getData, this)
        };
    },

    /**
     * This function keeps chain to release success/error function when all
     * functions will finish their job.
     *
     * @method next
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to next callback
     *                                      as parameters
    */
    next: function() {
        this.parrallelCount--;

        // We have to raise final callback (success or error)
        // The error function is managed by stop function
        if(this.parrallelCount == 0 && this.running) {
            this.running = false;
            this.dispatch('success');

            // We raise final success function
            if(a.isFunction(this.successFunction)) {
                var scope = this.resultScope || this.scope || this;
                this.successFunction.call(scope, this.getResultObject());
            }
        }
    },

    /**
     * Stop the callback chain.
     *
     * @method stop
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to error callback
     *                                      as parameters
    */
    stop: function() {
        this.parrallelCount = 0;
        var wasRunning      = this.running;
        this.running        = false;

        var scope  = this.resultScope || this.scope || this,
            args   = a.toArray(arguments);

        this.dispatch('error');
        if(wasRunning && a.isFunction(this.errorFunction)) {
            args.push(this.getResultObject());
            this.errorFunction.apply(scope, args);
        }
    },

    /**
     * Start chainer queue.
     *
     * @method start
     *
     * @method
    */
    start: function() {
        this.parrallelCount = this.callbacks.length;
        this.running = true;

        this.dispatch('start');

        // There is no callback, we directly jump on success
        if(this.parrallelCount <= 0) {
            // We fake parallel count to let next think it's a function
            // ending (normal process ending)
            this.parrallelCount = 1;
            this.next();
            return;
        }

        // For every callbacks existing, we start it
        var scope = this.scope || this,
            args  = a.toArray(arguments);

        args.push(this.getResultObject());

        for(var i=0, l=this.callbacks.length; i<l; ++i) {
            var callback = this.callbacks[i];
            callback.apply(scope, args);
        }
    },

    /**
     * Get if the chain system is currently running or not
     *
     * @method isRunning
     *
     * @return {Boolean}                    True: currently running
     *                                      False: currently stopped
    */
    isRunning: function() {
        return this.running;
    }
};

// Alias
a.callback.synchronizerInstance.prototype.success =
        a.callback.synchronizerInstance.prototype.next;
a.callback.synchronizerInstance.prototype.done    =
        a.callback.synchronizerInstance.prototype.next;
a.callback.synchronizerInstance.prototype.fail    =
        a.callback.synchronizerInstance.prototype.stop;
a.callback.synchronizerInstance.prototype.error   =
        a.callback.synchronizerInstance.prototype.stop;

/**
 * Load many functions one by one, when last one finish raise the final
 * callback
 *
 * @class chainer
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.chainer = function(callbacks, success, error) {
    return a.extend(
        new a.callback.chainerInstance(
            callbacks,
            success,
            error
        ),
        new a.eventEmitter('a.callback.chainer')
    );
};

/**
 * chainerInstance, NEVER use like this, use a.callback.chainer instead.
 *
 * @class chainerInstance
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.chainerInstance = function(callbacks, success, error) {
    this.callbacks       = callbacks || [];
    this.queue           = [];
    this.successFunction = success;
    this.errorFunction   = error;
    this.data            = {};
    this.resultScope     = null;
    this.scope           = null;
};

a.callback.chainerInstance.prototype = {
    /**
     * Add callback to existing callback list.
     * If the system is started, also append this callback to waiting queue.
     *
     * @method addCallback
     *
     * @param {Array}                       Any number of functions to chain
     *                                      The first function will be executed
     *                                      at first, and the last at last, in
     *                                      the order you give to that fct.
    */
    addCallback: function() {
        var args = a.toArray(arguments);

        this.callbacks = this.callbacks.concat(args);

        if(this.isRunning()) {
            this.queue = this.queue.concat(args);
        }
    },

    /**
     * Remove callback from existing callback list.
     *
     * @method removeCallback
     *
     * @param fct {Function}                The function to remove from list
    */
    removeCallback: function(fct) {
        this.callbacks = a.without(this.callbacks, fct);
        this.queue     = a.without(this.without, fct);
    },

    /**
     * Apply this scope to all callback function
     *
     * @method setScope
     *
     * @param scope {Object}                The scope to apply to callbacks
    */
    setScope: function(scope) {
        if(a.isTrueObject(scope)) {
            this.scope = scope;
        }
    },

    /**
     * Get a currently stored data.
     *
     * @method getData
     *
     * @param key {String}                  The key linked to value to get data
     * @return {Object | null}              The value previously stored and
     *                                      content
    */
    getData: function(key) {
        return this.data[key] || null;
    },

    /**
     * Set a new data stored into container
     *
     * @method setData
     *
     * @param key {String}                  The key to retrieve value later
     * @param value {Object}                Any value to store, a null or
     *                                      undefined element will erase key
     *                                      from store
    */
    setData: function(key, value) {
        if(a.isNone(value)) {
            delete this.data[key];
        } else {
            this.data[key] = value;
        }
    },

    /**
     * Get the main callback object to manipulate chain from it.
     *
     * @method getResultObject
     *
     * @return {Object}                     An object ready to use for
     *                                      controlling chain process
    */
    getResultObject: function() {
        return {
            next:    a.scope(this.next, this),
            done:    a.scope(this.next, this),
            success: a.scope(this.next, this),
            fail:    a.scope(this.stop, this),
            error:   a.scope(this.stop, this),
            stop:    a.scope(this.stop, this),
            setData: a.scope(this.setData, this),
            getData: a.scope(this.getData, this)
        };
    },

    /**
     * Go to the next function in callback chain.
     *
     * @method next
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to next callback
     *                                      as parameters
    */
    next: function() {
        var args = a.toArray(arguments),
            scope = this.scope || this;

        // We add at the end the chain/result object
        var that = this;
        args.push(this.getResultObject());

        // We stop if queue is ended
        if(!this.queue.length) {
            this.dispatch('success');

            // Success is now launched
            if(a.isFunction(this.successFunction)) {
                scope = this.resultScope || scope;
                this.successFunction.apply(scope, args);
            }
            return;
        }

        // Getting the callback
        var callback = this.queue.shift();
        if(a.isFunction(callback)) {
            // We transfert arguments from next to next callback
            callback.apply(scope, args);
        }
    },

    /**
     * Stop the callback chain.
     *
     * @method stop
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to error callback
     *                                      as parameters
    */
    stop: function() {
        this.queue = [];
        var scope  = this.scope || this,
            args   = a.toArray(arguments);

        this.dispatch('stop');
        if(a.isFunction(this.errorFunction)) {
            args.push(this.getResultObject());
            this.errorFunction.apply(scope, args);
        }
    },

    /**
     * Start chainer queue.
     *
     * @method start
    */
    start: function() {
        if(this.queue.length) {
            return;
        }

        // Preparing queue
        this.queue = a.deepClone(this.callbacks);
        this.dispatch('start');

        // Starting queue
        this.next();
    },

    /**
     * Get if the chain system is currently running or not
     *
     * @method isRunning
     *
     * @return {Boolean}                    True: currently running
     *                                      False: currently stopped
    */
    isRunning: function() {
        return this.queue.length ? true : false;
    }
};

// Alias
a.callback.chainerInstance.prototype.success =
        a.callback.chainerInstance.prototype.next;
a.callback.chainerInstance.prototype.done    =
        a.callback.chainerInstance.prototype.next;
a.callback.chainerInstance.prototype.fail    =
        a.callback.chainerInstance.prototype.stop;
a.callback.chainerInstance.prototype.error   =
        a.callback.chainerInstance.prototype.stop;
<commit_msg>jshint problems solved.<commit_after>/* ************************************************************************

    License: MIT Licence

    Description:
        Simple synchronizer/chainer for callback list of functions
        synchronizer : Load many functions at same time, when they all finish
                       raise the final callback
        chainer : Load many functions one by one, when last one finish raise
                  the final callback

************************************************************************ */

//Simple synchronizer/chainer for Array of functions
a.callback = {};

/**
 * Load many functions at same time,
 * when they all finish raise the final callback
 *
 * @class synchronizer
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.synchronizer = function(callbacks, success, error) {
    return a.extend(
            new a.callback.synchronizerInstance(
                callbacks,
                success,
                error
            ),
            new a.eventEmitter('a.callback.synchronizer')
        );
};

/**
 * synchronizerInstance, NEVER use like this,
 * use a.callback.synchronizer instead.
 *
 * @class synchronizerInstance
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.synchronizerInstance = function(callbacks, success, error) {
    this.callbacks       = callbacks || [];
    this.successFunction = success;
    this.errorFunction   = error;
    this.data            = {};
    this.resultScope     = null;
    this.scope           = null;
    this.parrallelCount  = 0;
    this.running         = false;
};

a.callback.synchronizerInstance.prototype = {
    /**
     * Add callback to existing callback list.
     * If the system is started, also append this callback to waiting queue.
     *
     * @method addCallback
     *
     * @param {Array}                       Any number of functions to chain
     *                                      The first function will be executed
     *                                      at first, and the last at last, in
     *                                      the order you give to that fct.
    */
    addCallback: function() {
        var args = a.toArray(arguments);

        this.callbacks = this.callbacks.concat(args);

        if(this.isRunning()) {
            var scope = this.scope || this,
                result = this.getResultObject();

            a.each(args, function(callback) {
                callback.call(scope, result);
            });
        }
    },

    /**
     * Remove callback from existing callback list.
     *
     * @method removeCallback
     *
     * @param fct {Function}                The function to remove from list
    */
    removeCallback: function(fct) {
        this.callbacks = a.without(this.callbacks, fct);
    },

    /**
     * Apply this scope to all callback function
     *
     * @method setScope
     *
     * @param scope {Object}                The scope to apply to callbacks
    */
    setScope: function(scope) {
        if(a.isTrueObject(scope)) {
            this.scope = scope;
        }
    },

    /**
     * Get a currently stored data.
     *
     * @method getData
     *
     * @param key {String}                  The key linked to value to get data
     * @return {Object | null}              The value previously stored and
     *                                      content
    */
    getData: function(key) {
        return this.data[key] || null;
    },

    /**
     * Set a new data stored into container
     *
     * @method setData
     *
     * @param key {String}                  The key to retrieve value later
     * @param value {Object}                Any value to store, a null or
     *                                      undefined element will erase key
     *                                      from store
    */
    setData: function(key, value) {
        if(a.isNone(value)) {
            delete this.data[key];
        } else {
            this.data[key] = value;
        }
    },

    /**
     * Get the main callback object to manipulate chain from it.
     *
     * @method getResultObject
     *
     * @return {Object}                     An object ready to use for
     *                                      controlling chain process
    */
    getResultObject: function() {
        var n = a.scope(this.next, this),
            s = a.scope(this.stop, this);
        return {
            next: n, done: n, success: n,
            fail: s, error: s, stop: s,
            setData: a.scope(this.setData, this),
            getData: a.scope(this.getData, this)
        };
    },

    /**
     * This function keeps chain to release success/error function when all
     * functions will finish their job.
     *
     * @method next
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to next callback
     *                                      as parameters
    */
    next: function() {
        this.parrallelCount--;

        // We have to raise final callback (success or error)
        // The error function is managed by stop function
        if(this.parrallelCount === 0 && this.running) {
            this.running = false;
            this.dispatch('success');

            // We raise final success function
            if(a.isFunction(this.successFunction)) {
                var scope = this.resultScope || this.scope || this;
                this.successFunction.call(scope, this.getResultObject());
            }
        }
    },

    /**
     * Stop the callback chain.
     *
     * @method stop
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to error callback
     *                                      as parameters
    */
    stop: function() {
        this.parrallelCount = 0;
        var wasRunning      = this.running;
        this.running        = false;

        var scope  = this.resultScope || this.scope || this,
            args   = a.toArray(arguments);

        this.dispatch('error');
        if(wasRunning && a.isFunction(this.errorFunction)) {
            args.push(this.getResultObject());
            this.errorFunction.apply(scope, args);
        }
    },

    /**
     * Start chainer queue.
     *
     * @method start
     *
     * @method
    */
    start: function() {
        this.parrallelCount = this.callbacks.length;
        this.running = true;

        this.dispatch('start');

        // There is no callback, we directly jump on success
        if(this.parrallelCount <= 0) {
            // We fake parallel count to let next think it's a function
            // ending (normal process ending)
            this.parrallelCount = 1;
            this.next();
            return;
        }

        // For every callbacks existing, we start it
        var scope = this.scope || this,
            args  = a.toArray(arguments);

        args.push(this.getResultObject());

        for(var i=0, l=this.callbacks.length; i<l; ++i) {
            var callback = this.callbacks[i];
            callback.apply(scope, args);
        }
    },

    /**
     * Get if the chain system is currently running or not
     *
     * @method isRunning
     *
     * @return {Boolean}                    True: currently running
     *                                      False: currently stopped
    */
    isRunning: function() {
        return this.running;
    }
};

// Alias
a.callback.synchronizerInstance.prototype.success =
        a.callback.synchronizerInstance.prototype.next;
a.callback.synchronizerInstance.prototype.done    =
        a.callback.synchronizerInstance.prototype.next;
a.callback.synchronizerInstance.prototype.fail    =
        a.callback.synchronizerInstance.prototype.stop;
a.callback.synchronizerInstance.prototype.error   =
        a.callback.synchronizerInstance.prototype.stop;

/**
 * Load many functions one by one, when last one finish raise the final
 * callback
 *
 * @class chainer
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.chainer = function(callbacks, success, error) {
    return a.extend(
        new a.callback.chainerInstance(
            callbacks,
            success,
            error
        ),
        new a.eventEmitter('a.callback.chainer')
    );
};

/**
 * chainerInstance, NEVER use like this, use a.callback.chainer instead.
 *
 * @class chainerInstance
 * @namespace a.callback
 * @constructor
 * @async
*/
a.callback.chainerInstance = function(callbacks, success, error) {
    this.callbacks       = callbacks || [];
    this.queue           = [];
    this.successFunction = success;
    this.errorFunction   = error;
    this.data            = {};
    this.resultScope     = null;
    this.scope           = null;
};

a.callback.chainerInstance.prototype = {
    /**
     * Add callback to existing callback list.
     * If the system is started, also append this callback to waiting queue.
     *
     * @method addCallback
     *
     * @param {Array}                       Any number of functions to chain
     *                                      The first function will be executed
     *                                      at first, and the last at last, in
     *                                      the order you give to that fct.
    */
    addCallback: function() {
        var args = a.toArray(arguments);

        this.callbacks = this.callbacks.concat(args);

        if(this.isRunning()) {
            this.queue = this.queue.concat(args);
        }
    },

    /**
     * Remove callback from existing callback list.
     *
     * @method removeCallback
     *
     * @param fct {Function}                The function to remove from list
    */
    removeCallback: function(fct) {
        this.callbacks = a.without(this.callbacks, fct);
        this.queue     = a.without(this.without, fct);
    },

    /**
     * Apply this scope to all callback function
     *
     * @method setScope
     *
     * @param scope {Object}                The scope to apply to callbacks
    */
    setScope: function(scope) {
        if(a.isTrueObject(scope)) {
            this.scope = scope;
        }
    },

    /**
     * Get a currently stored data.
     *
     * @method getData
     *
     * @param key {String}                  The key linked to value to get data
     * @return {Object | null}              The value previously stored and
     *                                      content
    */
    getData: function(key) {
        return this.data[key] || null;
    },

    /**
     * Set a new data stored into container
     *
     * @method setData
     *
     * @param key {String}                  The key to retrieve value later
     * @param value {Object}                Any value to store, a null or
     *                                      undefined element will erase key
     *                                      from store
    */
    setData: function(key, value) {
        if(a.isNone(value)) {
            delete this.data[key];
        } else {
            this.data[key] = value;
        }
    },

    /**
     * Get the main callback object to manipulate chain from it.
     *
     * @method getResultObject
     *
     * @return {Object}                     An object ready to use for
     *                                      controlling chain process
    */
    getResultObject: function() {
        return {
            next:    a.scope(this.next, this),
            done:    a.scope(this.next, this),
            success: a.scope(this.next, this),
            fail:    a.scope(this.stop, this),
            error:   a.scope(this.stop, this),
            stop:    a.scope(this.stop, this),
            setData: a.scope(this.setData, this),
            getData: a.scope(this.getData, this)
        };
    },

    /**
     * Go to the next function in callback chain.
     *
     * @method next
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to next callback
     *                                      as parameters
    */
    next: function() {
        var args = a.toArray(arguments),
            scope = this.scope || this;

        // We add at the end the chain/result object
        var that = this;
        args.push(this.getResultObject());

        // We stop if queue is ended
        if(!this.queue.length) {
            this.dispatch('success');

            // Success is now launched
            if(a.isFunction(this.successFunction)) {
                scope = this.resultScope || scope;
                this.successFunction.apply(scope, args);
            }
            return;
        }

        // Getting the callback
        var callback = this.queue.shift();
        if(a.isFunction(callback)) {
            // We transfert arguments from next to next callback
            callback.apply(scope, args);
        }
    },

    /**
     * Stop the callback chain.
     *
     * @method stop
     *
     * @param {Array}                       Any arguments given to that one
     *                                      will be transfert to error callback
     *                                      as parameters
    */
    stop: function() {
        this.queue = [];
        var scope  = this.scope || this,
            args   = a.toArray(arguments);

        this.dispatch('stop');
        if(a.isFunction(this.errorFunction)) {
            args.push(this.getResultObject());
            this.errorFunction.apply(scope, args);
        }
    },

    /**
     * Start chainer queue.
     *
     * @method start
    */
    start: function() {
        if(this.queue.length) {
            return;
        }

        // Preparing queue
        this.queue = a.deepClone(this.callbacks);
        this.dispatch('start');

        // Starting queue
        this.next();
    },

    /**
     * Get if the chain system is currently running or not
     *
     * @method isRunning
     *
     * @return {Boolean}                    True: currently running
     *                                      False: currently stopped
    */
    isRunning: function() {
        return this.queue.length ? true : false;
    }
};

// Alias
a.callback.chainerInstance.prototype.success =
        a.callback.chainerInstance.prototype.next;
a.callback.chainerInstance.prototype.done    =
        a.callback.chainerInstance.prototype.next;
a.callback.chainerInstance.prototype.fail    =
        a.callback.chainerInstance.prototype.stop;
a.callback.chainerInstance.prototype.error   =
        a.callback.chainerInstance.prototype.stop;
<|endoftext|>


<commit_before>/**
 * Created by sanchez
 */
'use strict';
window.game = new Phaser.Game('100', '100', Phaser.CANVAS, 'mycanvas', null, true);
window.game.state.add('Boot', require('./states/boot'));
window.game.state.add('Preloader', require('./states/preloader'));
window.game.state.add('State1', require('./states/state1'));
window.game.state.start('Boot');

window.h5 = {
    rootResize2: function() {
        //orientation landscape width=1334px
        var wFsize;
        var wWidth = (screen.width > 0) ? (window.innerWidth >= screen.width || window.innerWidth == 0) ? screen.width :
            window.innerWidth : window.innerWidth;
        var wHeight = (screen.height > 0) ? (window.innerHeight >= screen.height || window.innerHeight == 0) ?
            screen.height : window.innerHeight : window.innerHeight;
        if (wWidth > wHeight) {
            wHeight = wWidth;
        }
        wFsize = wHeight / 13.34;
        document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
    },
    rootResize1: function() {
        //orientation landscape width=1334px
        var that = this;
        var Dpr = 1,
            uAgent = window.navigator.userAgent;
        var isIOS = uAgent.match(/iphone/i);
        var isYIXIN = uAgent.match(/yixin/i);
        var is2345 = uAgent.match(/Mb2345/i);
        var ishaosou = uAgent.match(/mso_app/i);
        var isSogou = uAgent.match(/sogoumobilebrowser/ig);
        var isLiebao = uAgent.match(/liebaofast/i);
        var isGnbr = uAgent.match(/GNBR/i);
        var isWeixin = uAgent.match(/MicroMessenger/i);
        var wFsize;
        var wWidth = (screen.width > 0) ? (window.innerWidth >= screen.width || window.innerWidth == 0) ? screen.width :
            window.innerWidth : window.innerWidth;
        var wHeight = (screen.height > 0) ? (window.innerHeight >= screen.height || window.innerHeight == 0) ?
            screen.height : window.innerHeight : window.innerHeight;
        if (isIOS) {
            wWidth = screen.width;
            wHeight = screen.height;
        }
        if (wWidth > wHeight) {
            wHeight = wWidth;
        }
        wFsize = wHeight / 13.34;
        if (isYIXIN || is2345 || ishaosou || isSogou || isLiebao || isGnbr || isWeixin) { //YIXIN 和 2345 这里有个刚调用系统浏览器时候的bug，需要一点延迟来获取
            setTimeout(function() {
                wHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
                wWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
                if (wWidth > wHeight) {
                    wHeight = wWidth;
                }
                wFsize = wHeight / 13.34;
                // wFsize = wFsize > 32 ? wFsize : 32;
                document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
            }, 500);
        } else {
            document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
        }

        return that;

    },
    eventInit: function() {
        var that = this;
        document.addEventListener('touchstart', function(e) {}, false);
        document.addEventListener('touchmove', function(e) {
            e.preventDefault();
        }, false);
        return that;
    },
    cssInit: function() {
        var that = this;
        /*
        that.rootResize1();
        window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
            if (window.orientation == 90 || window.orientation == -90) {
                //横屏
                //_.renderShuping();
                that.rootResize();
            } else {
                //竖屏
                //_.closeShuping();
            }
        }, false);
        */
        var noChangeCountToEnd = 100,
            noEndTimeout = 1000;
        that.rootResize2();
        window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
            var interval,
                timeout,
                end,
                lastInnerWidth,
                lastInnerHeight,
                noChangeCount;
            end = function() {
                // "orientationchangeend"
                clearInterval(interval);
                clearTimeout(timeout);
                interval = null;
                timeout = null;
                that.rootResize1();
            };
            interval = setInterval(function() {
                if (window.innerWidth === lastInnerWidth && window.innerHeight === lastInnerHeight) {
                    noChangeCount++;
                    if (noChangeCount === noChangeCountToEnd) {
                        // The interval resolved the issue first.
                        end();
                    }
                } else {
                    lastInnerWidth = window.innerWidth;
                    lastInnerHeight = window.innerHeight;
                    noChangeCount = 0;
                }
            });
            timeout = setTimeout(function() {
                // The timeout happened first.
                end();
            }, noEndTimeout);
        });

        return that;
    }
};
window.h5.cssInit().eventInit();

function showStats() {
    var stats = new Stats();
    stats.showPanel(1); // 0: fps, 1: ms, 2: mb, 3+: custom
    var fs = document.createElement('div');
    fs.style.position = 'absolute';
    fs.style.left = 0;
    fs.style.top = 0;
    fs.style.zIndex = 999;
    fs.appendChild(stats.domElement);
    document.body.appendChild(fs);

    function animate() {
        stats.begin();
        // monitored code goes here
        stats.end();
        requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
}
showStats();<commit_msg>:wheelchair: Improving<commit_after>/**
 * Created by sanchez
 */
'use strict';
window.game = new Phaser.Game('100', '100', Phaser.CANVAS, 'mycanvas', null, true);
window.game.state.add('Boot', require('./states/boot'));
window.game.state.add('Preloader', require('./states/preloader'));
window.game.state.add('State1', require('./states/state1'));
window.game.state.start('Boot');

window.h5 = {
    rootResize2: function() {
        //orientation landscape width=1334px
        var wFsize;
        var wWidth = (screen.width > 0) ? (window.innerWidth >= screen.width || window.innerWidth == 0) ? screen.width :
            window.innerWidth : window.innerWidth;
        var wHeight = (screen.height > 0) ? (window.innerHeight >= screen.height || window.innerHeight == 0) ?
            screen.height : window.innerHeight : window.innerHeight;
        if (wWidth > wHeight) {
            wHeight = wWidth;
        }
        wFsize = wHeight / 13.34;
        document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
    },
    rootResize1: function() {
        //orientation landscape width=1334px
        var that = this;
        var Dpr = 1,
            uAgent = window.navigator.userAgent;
        var isIOS = uAgent.match(/iphone/i);
        var isYIXIN = uAgent.match(/yixin/i);
        var is2345 = uAgent.match(/Mb2345/i);
        var ishaosou = uAgent.match(/mso_app/i);
        var isSogou = uAgent.match(/sogoumobilebrowser/ig);
        var isLiebao = uAgent.match(/liebaofast/i);
        var isGnbr = uAgent.match(/GNBR/i);
        var isWeixin = uAgent.match(/MicroMessenger/i);
        var wFsize;
        var wWidth = (screen.width > 0) ? (window.innerWidth >= screen.width || window.innerWidth == 0) ? screen.width :
            window.innerWidth : window.innerWidth;
        var wHeight = (screen.height > 0) ? (window.innerHeight >= screen.height || window.innerHeight == 0) ?
            screen.height : window.innerHeight : window.innerHeight;
        if (isIOS) {
            wWidth = screen.width;
            wHeight = screen.height;
        }
        if (wWidth > wHeight) {
            wHeight = wWidth;
        }
        wFsize = wHeight / 13.34;
        if (isYIXIN || is2345 || ishaosou || isSogou || isLiebao || isGnbr || isWeixin) { //YIXIN 和 2345 这里有个刚调用系统浏览器时候的bug，需要一点延迟来获取
            setTimeout(function() {
                wHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
                wWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
                if (wWidth > wHeight) {
                    wHeight = wWidth;
                }
                wFsize = wHeight / 13.34;
                // wFsize = wFsize > 32 ? wFsize : 32;
                document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
            }, 500);
        } else {
            document.getElementsByTagName('html')[0].style.fontSize = wFsize + 'px';
        }

        return that;

    },
    eventInit: function() {
        var that = this;
        document.addEventListener('touchstart', function(e) {}, false);
        document.addEventListener('touchmove', function(e) {
            e.preventDefault();
        }, false);
        return that;
    },
    cssInit: function() {
        var that = this;
        /*
        that.rootResize1();
        window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
            if (window.orientation == 90 || window.orientation == -90) {
                //横屏
                //_.renderShuping();
                that.rootResize();
            } else {
                //竖屏
                //_.closeShuping();
            }
        }, false);
        */
        var noChangeCountToEnd = 100,
            noEndTimeout = 1000;
        that.rootResize2();
        window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
            var interval,
                timeout,
                end,
                lastInnerWidth,
                lastInnerHeight,
                noChangeCount;
            end = function() {
                // "orientationchangeend"
                clearInterval(interval);
                clearTimeout(timeout);
                interval = null;
                timeout = null;
                that.rootResize1();
            };
            interval = setInterval(function() {
                if (window.innerWidth === lastInnerWidth && window.innerHeight === lastInnerHeight) {
                    noChangeCount++;
                    if (noChangeCount === noChangeCountToEnd) {
                        // The interval resolved the issue first.
                        end();
                    }
                } else {
                    lastInnerWidth = window.innerWidth;
                    lastInnerHeight = window.innerHeight;
                    noChangeCount = 0;
                }
            });
            timeout = setTimeout(function() {
                // The timeout happened first.
                end();
            }, noEndTimeout);
        });

        return that;
    }
};
window.h5.cssInit().eventInit();

function showStats() {
    var stats = new Stats();
    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    var fs = document.createElement('div');
    fs.style.position = 'absolute';
    fs.style.left = 0;
    fs.style.top = 0;
    fs.style.zIndex = 999;
    fs.appendChild(stats.domElement);
    document.body.appendChild(fs);

    function animate() {
        stats.begin();
        // monitored code goes here
        stats.end();
        requestAnimationFrame(animate);
    }
    requestAnimationFrame(animate);
}
showStats();<|endoftext|>


<commit_before>module.exports = {
  src: './*.css',
  options: {
    ignorePath: './.linterignore',
  },
};
<commit_msg>edit the stylelint.js file<commit_after>module.exports = {
  src: './**/*.css',
  options: {
    ignorePath: './.linterignore',
  },
};
<|endoftext|>


<commit_before>var Scientilla = Scientilla || {};

Scientilla.user = {
    getAliases: function () {
        var firstLetter = function (string) {
            if (!string)
                return "";
            return string.charAt(0).toUpperCase();
        };
        var capitalize = function (string) {
            if (!string)
                return "";
            return _.capitalize(string.toLowerCase());
//                return str.replace(/\w\S*/g, function (txt) {
//                    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
//                });
        };
        var aliases = [];
        if (_.isEmpty(this.name) || _.isEmpty(this.surname))
            return aliases;
        var first_name = capitalize(this.name);
        var last_name = capitalize(this.surname);
        var initial_first_name = firstLetter(first_name);
        aliases.push(first_name + " " + last_name);
        aliases.push(last_name + " " + first_name);
        aliases.push(last_name + " " + initial_first_name + ".");
        aliases.push(initial_first_name + ". " + last_name + "");
        aliases = _.uniq(aliases);
        return aliases;
    },
    getUcAliases: function () {
        var aliases = this.getAliases();
        var ucAliases = _.map(aliases, function (a) {
            return a.toUpperCase();
        });
        return ucAliases;
    },
    getDisplayName: function () {
        var name = this.name ? this.name : "";
        var surname = this.surname ? this.surname : "";
        var fullName = _.trim(name + " " + surname);
        return fullName;
    },
    getType: function() {
        return 'user';
    }
};

//sTodo: evaluating wheter to reformat in a constructor style
Scientilla.reference = {
    UNKNOWN_REFERENCE:0,
    USER_REFERENCE: 1,
    GROUP_REFERENCE: 2,
    DRAFT: 'draft',
    VERIFIED: 'verified',
    PUBLIC: 'public',
    create: function (referenceData, owner) {
        var fields = [
            'authors',
            'title',
            'status'
        ];
        var reference = _.pick(referenceData, fields);
        if (owner.getType() === 'user')
            reference.owner = owner.id;
        else
            reference.groupOwner = owner.id;
        _.extend(reference, Scientilla.reference);
        return reference;
    },
    getRealAuthors: function () {
        var realAuthors = _.clone(this.collaborators);
        if (this.hasRealOwner())
            realAuthors.push(this.owner);
        return realAuthors;

    },
    getCollaborations: function () {
        var collaborations = _.clone(this.groupCollaborations);
        if (this.hasRealGroupOwner())
            collaborations.push(this.groupOwner);
        return collaborations;
    },
    getType: function () {
        if (!!this.owner)
            return this.USER_REFERENCE;
        else if (!!this.groupOwner)
            return this.GROUP_REFERENCE;
        else
            return this.UNKNOWN_REFERENCE;
    },
    getAuthors: function () {
        if (!this.authors)
            return [];

        return this.authors.replace(/\s+et all\s*$/i, '').split(',').map(_.trim);
    },
    getUcAuthors: function () {
        var authors = this.getAuthors();
        var ucAuthors = _.map(authors, function (a) {
            return a.toUpperCase();
        });
        return ucAuthors;
    },
    hasRealOwner: function () {
        return _.isObject(this.owner);
    },
    hasRealGroupOwner: function () {
        return _.isObject(this.groupOwner);
    },
    getDisplayName: function () {
        return this.getDisplayName();
    },
    getNewGroupReference: function (groupId) {
        return {
            title: "",
            authors: "",
            groupOwner: groupId,
            status: Scientilla.reference.DRAFT
        };
    }
};

Scientilla.membership = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.user.getDisplayName))
            return this.user.getDisplayName();
        else
            return '';
    }
};

Scientilla.group = {
    getDisplayName: function() {
        return this.name;
    },
    getType: function() {
        return 'group';
    }
};<commit_msg>Added roles constants to the user model.<commit_after>var Scientilla = Scientilla || {};

Scientilla.user = {
    USER: 'user',
    ADMINISTRATOR: 'administrator',
    getAliases: function () {
        var firstLetter = function (string) {
            if (!string)
                return "";
            return string.charAt(0).toUpperCase();
        };
        var capitalize = function (string) {
            if (!string)
                return "";
            return _.capitalize(string.toLowerCase());
//                return str.replace(/\w\S*/g, function (txt) {
//                    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
//                });
        };
        var aliases = [];
        if (_.isEmpty(this.name) || _.isEmpty(this.surname))
            return aliases;
        var first_name = capitalize(this.name);
        var last_name = capitalize(this.surname);
        var initial_first_name = firstLetter(first_name);
        aliases.push(first_name + " " + last_name);
        aliases.push(last_name + " " + first_name);
        aliases.push(last_name + " " + initial_first_name + ".");
        aliases.push(initial_first_name + ". " + last_name + "");
        aliases = _.uniq(aliases);
        return aliases;
    },
    getUcAliases: function () {
        var aliases = this.getAliases();
        var ucAliases = _.map(aliases, function (a) {
            return a.toUpperCase();
        });
        return ucAliases;
    },
    getDisplayName: function () {
        var name = this.name ? this.name : "";
        var surname = this.surname ? this.surname : "";
        var fullName = _.trim(name + " " + surname);
        return fullName;
    },
    getType: function() {
        return 'user';
    }
};

//sTodo: evaluating wheter to reformat in a constructor style
Scientilla.reference = {
    UNKNOWN_REFERENCE:0,
    USER_REFERENCE: 1,
    GROUP_REFERENCE: 2,
    DRAFT: 'draft',
    VERIFIED: 'verified',
    PUBLIC: 'public',
    create: function (referenceData, owner) {
        var fields = [
            'authors',
            'title',
            'status'
        ];
        var reference = _.pick(referenceData, fields);
        if (owner.getType() === 'user')
            reference.owner = owner.id;
        else
            reference.groupOwner = owner.id;
        _.extend(reference, Scientilla.reference);
        return reference;
    },
    getRealAuthors: function () {
        var realAuthors = _.clone(this.collaborators);
        if (this.hasRealOwner())
            realAuthors.push(this.owner);
        return realAuthors;

    },
    getCollaborations: function () {
        var collaborations = _.clone(this.groupCollaborations);
        if (this.hasRealGroupOwner())
            collaborations.push(this.groupOwner);
        return collaborations;
    },
    getType: function () {
        if (!!this.owner)
            return this.USER_REFERENCE;
        else if (!!this.groupOwner)
            return this.GROUP_REFERENCE;
        else
            return this.UNKNOWN_REFERENCE;
    },
    getAuthors: function () {
        if (!this.authors)
            return [];

        return this.authors.replace(/\s+et all\s*$/i, '').split(',').map(_.trim);
    },
    getUcAuthors: function () {
        var authors = this.getAuthors();
        var ucAuthors = _.map(authors, function (a) {
            return a.toUpperCase();
        });
        return ucAuthors;
    },
    hasRealOwner: function () {
        return _.isObject(this.owner);
    },
    hasRealGroupOwner: function () {
        return _.isObject(this.groupOwner);
    },
    getDisplayName: function () {
        return this.getDisplayName();
    },
    getNewGroupReference: function (groupId) {
        return {
            title: "",
            authors: "",
            groupOwner: groupId,
            status: Scientilla.reference.DRAFT
        };
    }
};

Scientilla.membership = {
    getDisplayName: function () {
        //sTODO: to be removed when deep populate is implemented
        if (_.isFunction(this.user.getDisplayName))
            return this.user.getDisplayName();
        else
            return '';
    }
};

Scientilla.group = {
    getDisplayName: function() {
        return this.name;
    },
    getType: function() {
        return 'group';
    }
};<|endoftext|>


<commit_before>/**
 * This file is part of Shoop.
 *
 * Copyright (c) 2012-2015, Shoop Ltd. All rights reserved.
 *
 * This source code is licensed under the AGPLv3 license found in the
 * LICENSE file in the root directory of this source tree.
 */
$(function() {
    $(".product-media-delete").on("click", function(e) {
        e.preventDefault();
        if (confirm("Are you sure you want to delete this media?"))
        {
            $(this).parents(".panel").fadeOut();
            $(this).next(".hide").find("input").prop("checked", true);
        }
    });

    $(document).on("click", ".set-as-primary", function(e) {
        e.preventDefault();
        const $panel = $(this).parents(".panel");
        const prefix = $panel.data("prefix");

        const [, current] = prefix.split("-");

        const $imagePanels = $("#product-images-section .panel");

        $imagePanels.removeClass("panel-selected").addClass("panel-default");

        $(".is-primary-image").replaceWith(function() {
            return $("<a>", {"class": "set-as-primary", "href": "#"}).text("Set as primary image");
        });

        $imagePanels.each(function(i) {
            $("#id_images-" + i + "-is_primary").prop("checked", false);
        });

        $(this).replaceWith(function() {
            return $("<span>", {"class": "is-primary-image"}).text("Primary image");
        });

        $panel.addClass("panel-selected");
        $("#id_images-" + current + "-is_primary").prop("checked", true);
    });

    $(".media-add-new-panel").on("click", function(e) {
        e.preventDefault();
        const panelCount = $("#" + $(this).data("target-panels") + " .panel").length;
        const $source = $("#placeholder-panel");
        const html = $source.html().replace(/__prefix__/g, panelCount - 1).replace(/__prefix_name__/g, panelCount);

        $(html).insertBefore($source);
        const targetId = $(this).data("target-id");
        const $totalFormsField = $("#" + targetId + "-TOTAL_FORMS");
        $totalFormsField.val(parseInt($totalFormsField.val()) + 1);
    });
});
<commit_msg>Product media edit: make js strings translatable<commit_after>/**
 * This file is part of Shoop.
 *
 * Copyright (c) 2012-2015, Shoop Ltd. All rights reserved.
 *
 * This source code is licensed under the AGPLv3 license found in the
 * LICENSE file in the root directory of this source tree.
 */
$(function() {
    $(".product-media-delete").on("click", function(e) {
        e.preventDefault();
        if (confirm(gettext("Are you sure you want to delete this media?")))
        {
            $(this).parents(".panel").fadeOut();
            $(this).next(".hide").find("input").prop("checked", true);
        }
    });

    $(document).on("click", ".set-as-primary", function(e) {
        e.preventDefault();
        const $panel = $(this).parents(".panel");
        const prefix = $panel.data("prefix");

        const [, current] = prefix.split("-");

        const $imagePanels = $("#product-images-section .panel");

        $imagePanels.removeClass("panel-selected").addClass("panel-default");

        $(".is-primary-image").replaceWith(function() {
            return $("<a>", {"class": "set-as-primary", "href": "#"}).text(gettext("Set as primary image"));
        });

        $imagePanels.each(function(i) {
            $("#id_images-" + i + "-is_primary").prop("checked", false);
        });

        $(this).replaceWith(function() {
            return $("<span>", {"class": "is-primary-image"}).text(gettext("Primary image"));
        });

        $panel.addClass("panel-selected");
        $("#id_images-" + current + "-is_primary").prop("checked", true);
    });

    $(".media-add-new-panel").on("click", function(e) {
        e.preventDefault();
        const panelCount = $("#" + $(this).data("target-panels") + " .panel").length;
        const $source = $("#placeholder-panel");
        const html = $source.html().replace(/__prefix__/g, panelCount - 1).replace(/__prefix_name__/g, panelCount);

        $(html).insertBefore($source);
        const targetId = $(this).data("target-id");
        const $totalFormsField = $("#" + targetId + "-TOTAL_FORMS");
        $totalFormsField.val(parseInt($totalFormsField.val()) + 1);
    });
});
<|endoftext|>


<commit_before>// adapted from:
// https://jakearchibald.com/2013/animated-line-drawing-svg/

var draw = function(mySvgObj, myPath){
	var svgObj, svgDoc;

	svgObj = document.getElementById(mySvgObj);
	svgDoc = svgObj.contentDocument;

	var path = svgDoc.getElementById(myPath);
	var length = path.getTotalLength();
	// Clear any previous transition
	path.style.transition = path.style.WebkitTransition = 'none';
	// Set up the starting positions
	path.style.strokeDasharray = length + ' ' + length;
	path.style.strokeDashoffset = length;
	// Trigger a layout so styles are calculated & the browser
	// picks up the starting position before animating
	path.getBoundingClientRect();
	// Define our transition
	path.style.transition = path.style.WebkitTransition =
	  'stroke-dashoffset 10s linear';
	// Go!
	path.style.strokeDashoffset = '0';
}

window.onload = function(){
	draw('driveRouteObj', 'drivePath');
}<commit_msg>newline at end of file<commit_after>// adapted from:
// https://jakearchibald.com/2013/animated-line-drawing-svg/

var draw = function(mySvgObj, myPath){
	var svgObj, svgDoc;

	svgObj = document.getElementById(mySvgObj);
	svgDoc = svgObj.contentDocument;

	var path = svgDoc.getElementById(myPath);
	var length = path.getTotalLength();
	// Clear any previous transition
	path.style.transition = path.style.WebkitTransition = 'none';
	// Set up the starting positions
	path.style.strokeDasharray = length + ' ' + length;
	path.style.strokeDashoffset = length;
	// Trigger a layout so styles are calculated & the browser
	// picks up the starting position before animating
	path.getBoundingClientRect();
	// Define our transition
	path.style.transition = path.style.WebkitTransition =
	  'stroke-dashoffset 10s linear';
	// Go!
	path.style.strokeDashoffset = '0';
}

window.onload = function(){
	draw('driveRouteObj', 'drivePath');
}
<|endoftext|>


<commit_before>/**
 * Model for book
 */
window.Book = Backbone.Model.extend({
    defaults: {
        'type': 'book',
        'public': true
    },
    url: function(){
        return '/' + window.simpleshelf.info.dbName + '/' + this.get('id');
    },
    initialize: function(attributes){
        console.log('Book', 'initialize');
        _.bindAll(this,
            "addActivity",
            "getDefault",
            "getStatus",
            "getStatusComplete",
            "select",
            "setStatus"
        );
        // last pieces to fire before this model finishes loading
        // verify status has minimum defaults
        this.set('status', _.extend({}, {'ownership': null, 'read': null}, this.get('status')));
        // if activities is missing, add a new instance: if set in this.defaults, it violates encapsulation
        if (!this.get('activities')){
            this.set('activities', new ActivityList([]));
        }
    },

    /**
     * Add an activity in one call
     * @param opts {Object} {date: date string, action: String}, IOW a valid Activity object's attributes
     **/
    addActivity: function(opts){
        this.get('activities').add(opts);
    },

    /**
     * Return the authors as a string, optionally delimited ()
     * @param opts {Object} {delimiter: String}
     * @return String
     */
    getAuthorsAsString: function(opts){
        if (!this.get('authors')){
            return "";
        }

        if (opts && opts.delimiter){
            return this.get('authors').join(opts.delimiter);
        } else {
            return this.get('authors').join(" ");
        }
    },

    /**
     * Provide a default (empty) value for a given attribute
     * @param opts {Object} {attr:[status]}
     */
    getDefault: function(opts){
        if (!opts || !_.has(opts, 'attr')){
            return null;
        }

        var result = null;
        switch(opts.attr){
            case "status":
                result = {'read': null, 'ownership': null};
                break;

            default:
                break;
        }

        return result;
    },

    getStatus: function(status){
        var statusHash = this.get('status') || {};
        if (_.has(statusHash, status)){
            return statusHash[status];
        } else {
            return null;
        }
    },

    /**
     * Get the complete status object, using the default as a base
     */
    getStatusComplete: function(){
        var result = _.extend({},
            this.getDefault({'attr': 'status'}),
            this.get('status')
        );
        return result;
    },

    /**
     * Parse book info, removing activities list, to be parsed by child collection
     * Inspired by http://stackoverflow.com/questions/8501170/backbone-js-view-of-model-containing-collection
     */
    parse: function(resp){
        if (_.has(resp, 'ok') && resp.ok == true){
            // no need to parse
            return;
        }
        if (this.attributes.activities){
            this.attributes.activities.reset(resp.activities);
            delete resp.activities;
        } else {
            resp.activities = new ActivityList(resp.activities);
        }

        // enforce deeper attributes
        resp.status = _.extend(this.getDefault({attr: 'status'}), resp.status);

        return resp;
    },

    select: function(){
        console.log('Book.select');
        this.collection.trigger('book:selected', this.get('id'));
    },

    setStatus: function(statusName, statusValue){
        var updatedStatus = {};
        updatedStatus[statusName] = statusValue;
        this.set(_.extend({}, this.get('status'), updatedStatus));
    },

    validate: function(attrs){
        // MUST HAVE EITHER title OR isbn: 5
        var hasValidTitle = false;
        var hasValidIsbn = false;
        if (_.has(attrs, 'title') &&
            _.isString(attrs.title) &&
            $.trim(attrs.title).length > 0){
            hasValidTitle = true;
        }
        if (_.has(attrs, 'isbn') &&
            _.isString(attrs.isbn) &&
            $.trim(attrs.isbn).length > 0){
            hasValidIsbn = true;
        }
        if (!hasValidTitle && !hasValidIsbn){
            return 5; //"must have either title or isbn"
        }
    }
});
<commit_msg>Set book status properly, preserving existing data<commit_after>/**
 * Model for book
 */
window.Book = Backbone.Model.extend({
    defaults: {
        'type': 'book',
        'public': true
    },
    url: function(){
        return '/' + window.simpleshelf.info.dbName + '/' + this.get('id');
    },
    initialize: function(attributes){
        console.log('Book', 'initialize');
        _.bindAll(this,
            "addActivity",
            "getDefault",
            "getStatus",
            "getStatusComplete",
            "select",
            "setStatus"
        );
        // last pieces to fire before this model finishes loading
        // verify status has minimum defaults
        this.set('status', _.extend({}, {'ownership': null, 'read': null}, this.get('status')));
        // if activities is missing, add a new instance: if set in this.defaults, it violates encapsulation
        if (!this.get('activities')){
            this.set('activities', new ActivityList([]));
        }
    },

    /**
     * Add an activity in one call
     * @param opts {Object} {date: date string, action: String}, IOW a valid Activity object's attributes
     **/
    addActivity: function(opts){
        this.get('activities').add(opts);
    },

    /**
     * Return the authors as a string, optionally delimited ()
     * @param opts {Object} {delimiter: String}
     * @return String
     */
    getAuthorsAsString: function(opts){
        if (!this.get('authors')){
            return "";
        }

        if (opts && opts.delimiter){
            return this.get('authors').join(opts.delimiter);
        } else {
            return this.get('authors').join(" ");
        }
    },

    /**
     * Provide a default (empty) value for a given attribute
     * @param opts {Object} {attr:[status]}
     */
    getDefault: function(opts){
        if (!opts || !_.has(opts, 'attr')){
            return null;
        }

        var result = null;
        switch(opts.attr){
            case "status":
                result = {'read': null, 'ownership': null};
                break;

            default:
                break;
        }

        return result;
    },

    getStatus: function(status){
        var statusHash = this.get('status') || {};
        if (_.has(statusHash, status)){
            return statusHash[status];
        } else {
            return null;
        }
    },

    /**
     * Get the complete status object, using the default as a base
     */
    getStatusComplete: function(){
        var result = _.extend({},
            this.getDefault({'attr': 'status'}),
            this.get('status')
        );
        return result;
    },

    /**
     * Parse book info, removing activities list, to be parsed by child collection
     * Inspired by http://stackoverflow.com/questions/8501170/backbone-js-view-of-model-containing-collection
     */
    parse: function(resp){
        if (_.has(resp, 'ok') && resp.ok == true){
            // no need to parse
            return;
        }
        if (this.attributes.activities){
            this.attributes.activities.reset(resp.activities);
            delete resp.activities;
        } else {
            resp.activities = new ActivityList(resp.activities);
        }

        // enforce deeper attributes
        resp.status = _.extend(this.getDefault({attr: 'status'}), resp.status);

        return resp;
    },

    select: function(){
        console.log('Book.select');
        this.collection.trigger('book:selected', this.get('id'));
    },

    /**
     * Carefully set the status, to ensure "default" values are preserved
     */
    setStatus: function(statusName, statusValue){
        var updatedStatus = {};
        updatedStatus[statusName] = statusValue;
        var finalValue = {'status': _.extend({}, this.get('status'), updatedStatus)};
        this.set(finalValue);
    },

    validate: function(attrs){
        // MUST HAVE EITHER title OR isbn: 5
        var hasValidTitle = false;
        var hasValidIsbn = false;
        if (_.has(attrs, 'title') &&
            _.isString(attrs.title) &&
            $.trim(attrs.title).length > 0){
            hasValidTitle = true;
        }
        if (_.has(attrs, 'isbn') &&
            _.isString(attrs.isbn) &&
            $.trim(attrs.isbn).length > 0){
            hasValidIsbn = true;
        }
        if (!hasValidTitle && !hasValidIsbn){
            return 5; //"must have either title or isbn"
        }
    }
});
<|endoftext|>


<commit_before>(function(window) {

  var COLORS = {};

  function addColorPalette(id, base, core, west, east) {
    COLORS[id] = {base:base, core:core, west:west, east:east};
  }

  addColorPalette('main',
    '#1E1A31', // Base
    '#FFFFFF', // Core
  [
    '#6B2A64',
    '#B14B77',
    '#E2687C',
    '#F98A75',
    '#FFB46B',
    '#FFE193'
  ],[
    '#4C4690',
    '#4D64B0',
    '#518CBE',
    '#5BAFCD',
    '#72D4E2',
    '#B9F7F5'
  ]);

  Sketch.create({

    setup: function() {
      this.container = document.getElementById('container');
      this.container.style.backgroundColor = COLORS.main.base;
      this.reset();
    },

    reset: function() {
      this.wave = [];
    },

    update: function() {
    },

    draw: function() {
    }

  });

})(window);
<commit_msg>:gb: Implemented random wave form for testing<commit_after>(function(window) {

  var WEST = 'west',
      EAST = 'east',
      CORE = 'core',
      COLORS = {};

  function addColorPalette(id, base, core, west, east) {
    COLORS[id] = {base:base, core:core, west:west, east:east};
  }

  addColorPalette('main',
    '#1E1A31', // Base
    '#FFFFFF', // Core
  [
    '#6B2A64',
    '#B14B77',
    '#E2687C',
    '#F98A75',
    '#FFB46B',
    '#FFE193'
  ],[
    '#4C4690',
    '#4D64B0',
    '#518CBE',
    '#5BAFCD',
    '#72D4E2',
    '#B9F7F5'
  ]);

  Sketch.create({

    interval: 1,
    container: document.getElementById('container'),

    setup: function() {
      this.setColorPalette(COLORS.main);
      this.reset();
    },

    setColorPalette: function(palette) {
      if (this.palette !== palette) {
        this.palette = palette;
        console.log(this);
        this.container.style.backgroundColor = palette.base;
      }
    },

    reset: function() {
      this.wave = [Math.random()];
    },

    resize: function() {
      this.center = Math.round(this.width * 0.5);
    },

    update: function() {
      var sign = Math.random() > 0.5 ? 1 : -1;
      var value = this.wave[this.wave.length-1];
      value += Math.random() * sign * 0.05;
      value = Math.max(value, 0);
      value = Math.min(value, 1);
      this.wave.push(value);
      if (this.wave.length > 100) {
        this.wave.shift();
      }
    },

    draw: function() {
      var i, l;
      for (i = 0, l = this.palette.west.length; i < l; i++) {
      }
      for (i = 0, l = this.palette.east.length; i < l; i++) {
      }
      this.drawWave(WEST, 0.8, 20, this.palette.core);
    },

    drawWave: function(state, scale, step, color) {
      var c1x, c1y, c2x, c2y;
      var i = this.wave.length - 1;
      var x = Math.round(this.center + this.center * this.wave[i] * scale);
      var y = this.height;
      this.beginPath();
      this.moveTo(this.center, y);
      this.lineTo(x, y);
      for (i; i >= 0; i--) {
        c1x = x;
        c1y = y - step * 0.5;
        c2x = Math.round(this.center + this.center * this.wave[i] * scale);
        c2y = y - step * 0.5;
        y = y - step;
        x = c2x;
        this.bezierCurveTo(c1x, c1y, c2x, c2y, x, y);
        if (y < 0) break;
      }
      this.lineTo(this.center, y);
      this.closePath();
      this.fillStyle = color;
      this.fill();
    }
  });

})(window);
<|endoftext|>


<commit_before>'use strict';

var d3 = require('d3');
var MG = require('../components/metricsgraphics');

exports.large = function(options, paydash) {

    MG.data_graphic({
        // title: options.title,
        title: '',
        data: options.data,
        width: 600,
        height: 500,
        left: 100,
        full_width: true,
        decimals: 0,
        target: options.target,
        baselines: [{
            value: 15,
            label: 'Days permitted'
        }],
        xax_count: 15,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        show_secondary_x_label: false,
        legend: options.labels,
        legend_target: options.legend_target,
        show_tooltips: false,
        aggregate_rollover: true,
        show_year_markers: true,
        transition_on_update: false,
        interplate: 'linear',
        interpolate_tension: 1,
        area: options.area,
        y_label: 'Days to Complete Process',
        mouseover: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            if (options.data.length) {
                for (i = 1; i <= options.data.length; i++) {
                    var l_span = d3.select(options.legend_target + ' .mg-line' + i + '-legend-color');
                    l_span.text(' ');
                    l_span.text('— ' + paydash[options.legend_labels][i - 1]);
                }
            }
            d.values.forEach(function(val, index) {
                var prefix = d3.formatPrefix(val.value);
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                // console.log(d.values[index-1]);
                var no_days = d.values[index - 1] ? prefix.scale(val.value - d.values[index - 1].value).toFixed(0) : prefix.scale(val.value).toFixed(0);
                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1] + ' : ' + no_days);
                var format = d3.time.format('%b %d, %Y');
                d3.select(options.target + '_total_trans').text(format(val.date) + ': ' + val.total_trans);
            });
        },
        mouseout: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            d.values.forEach(function(val, index) {
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][index]);
            });
        }
    });
};

exports.small = function(options, paydash) {
    MG.data_graphic({
        title: options.title,
        data: options.data,
        width: 295,
        height: 300,
        right: 10,
        left: 90,
        small_text: true,
        xax_count: 3,
        decimals: 0,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        target: options.target,
        legend: options.labels,
        legend_target: options.legend_target,
        full_width: true,
        aggregate_rollover: true,
        transition_on_update: false,
        max_y: options.max_y || undefined,
        interplate: 'linear',
        linked: true,
        interpolate_tension: 1,
        y_label: 'Days to Complete Process',
        area: options.area,
        show_secondary_x_label: false,
        max_x: options.max_x || undefined,
        min_x: options.min_x || undefined
    });
};

exports.large_indi = function(options, paydash) {

    MG.data_graphic({
        // title: options.title,
        title: '',
        data: options.data,
        width: 600,
        height: 500,
        left: 100,
        full_width: true,
        decimals: 0,
        target: options.target,
        xax_count: 15,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        show_secondary_x_label : false,
        legend: options.labels,
        legend_target: options.legend_target,
        show_tooltips: false,
        aggregate_rollover: true,
        show_year_markers: true,
        transition_on_update: false,
        interplate: 'linear',
        interpolate_tension: 1,
        area: options.area,
        y_label: 'Days to Complete Process',
        mouseover: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            if(options.data.length){
                for(i=1; i<=options.data.length;i++){
                    var l_span = d3.select(options.legend_target + ' .mg-line' + i + '-legend-color');
                    l_span.text(' ');
                    l_span.text('— ' + paydash[options.legend_labels][i - 1]);
                }
            }
            d.values.forEach(function(val, index) {
                var prefix = d3.formatPrefix(val.value);
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1] + ' : ' + prefix.scale(val.value).toFixed(0));
                var format = d3.time.format('%b %d, %Y');
                d3.select(options.target + '_total_trans').text(format(val.date) + ': ' + val.total_trans);
            });
        },
        mouseout: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            d.values.forEach(function(val, index) {
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1]);
            });
        }
    });
};
<commit_msg>Fixing rounding off big<commit_after>'use strict';

var d3 = require('d3');
var MG = require('../components/metricsgraphics');

exports.large = function(options, paydash) {

    MG.data_graphic({
        // title: options.title,
        title: '',
        data: options.data,
        width: 600,
        height: 500,
        left: 100,
        full_width: true,
        decimals: 0,
        target: options.target,
        baselines: [{
            value: 15,
            label: 'Days permitted'
        }],
        xax_count: 15,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        show_secondary_x_label: false,
        legend: options.labels,
        legend_target: options.legend_target,
        show_tooltips: false,
        aggregate_rollover: true,
        show_year_markers: true,
        transition_on_update: false,
        interplate: 'linear',
        interpolate_tension: 1,
        area: options.area,
        y_label: 'Days to Complete Process',
        mouseover: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            if (options.data.length) {
                for (i = 1; i <= options.data.length; i++) {
                    var l_span = d3.select(options.legend_target + ' .mg-line' + i + '-legend-color');
                    l_span.text(' ');
                    l_span.text('— ' + paydash[options.legend_labels][i - 1]);
                }
            }
            d.values.forEach(function(val, index) {
                // var prefix = d3.formatPrefix(val.value);
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                var no_days = d.values[index - 1] ? (val.value - d.values[index - 1].value).toFixed(0) : (val.value).toFixed(0);

                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1] + ' : ' + no_days);
                var format = d3.time.format('%b %d, %Y');
                d3.select(options.target + '_total_trans').text(format(val.date) + ': ' + val.total_trans);
            });
        },
        mouseout: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            d.values.forEach(function(val, index) {
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][index]);
            });
        }
    });
};

exports.small = function(options, paydash) {
    MG.data_graphic({
        title: options.title,
        data: options.data,
        width: 295,
        height: 300,
        right: 10,
        left: 90,
        small_text: true,
        xax_count: 3,
        decimals: 0,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        target: options.target,
        legend: options.labels,
        legend_target: options.legend_target,
        full_width: true,
        aggregate_rollover: true,
        transition_on_update: false,
        max_y: options.max_y || undefined,
        interplate: 'linear',
        linked: true,
        interpolate_tension: 1,
        y_label: 'Days to Complete Process',
        area: options.area,
        show_secondary_x_label: false,
        max_x: options.max_x || undefined,
        min_x: options.min_x || undefined
    });
};

exports.large_indi = function(options, paydash) {

    MG.data_graphic({
        // title: options.title,
        title: '',
        data: options.data,
        width: 600,
        height: 500,
        left: 100,
        full_width: true,
        decimals: 0,
        target: options.target,
        xax_count: 15,
        xax_format: d3.time.format('%e %b, %y'),
        chart_type: options.data.length !== 0 ? 'line' : 'missing-data',
        missing_text: 'No data',
        show_secondary_x_label : false,
        legend: options.labels,
        legend_target: options.legend_target,
        show_tooltips: false,
        aggregate_rollover: true,
        show_year_markers: true,
        transition_on_update: false,
        interplate: 'linear',
        interpolate_tension: 1,
        area: options.area,
        y_label: 'Days to Complete Process',
        mouseover: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            if(options.data.length){
                for(i=1; i<=options.data.length;i++){
                    var l_span = d3.select(options.legend_target + ' .mg-line' + i + '-legend-color');
                    l_span.text(' ');
                    l_span.text('— ' + paydash[options.legend_labels][i - 1]);
                }
            }
            d.values.forEach(function(val, index) {
                var prefix = d3.formatPrefix(val.value);
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1] + ' : ' + prefix.scale(val.value).toFixed(0));
                var format = d3.time.format('%b %d, %Y');
                d3.select(options.target + '_total_trans').text(format(val.date) + ': ' + val.total_trans);
            });
        },
        mouseout: function(d, i) {
            if (!d.values) {
                d.values = [d];
            }
            d.values.forEach(function(val, index) {
                var l_span = d3.select(options.legend_target + ' .mg-line' + val.line_id + '-legend-color');
                l_span.text(' ');
                l_span.text('— ' + paydash[options.legend_labels][val.line_id - 1]);
            });
        }
    });
};
<|endoftext|>


<commit_before>import TriggerButton from './TriggerButton';
import PopupModal from './PopupModal';
import setupGhostApi from '../utils/api';
import {ParentContext} from './ParentContext';

const React = require('react');

export default class ParentContainer extends React.Component {
    constructor(props) {
        super(props);

        // Setup custom trigger button handling
        this.setupCustomTriggerButton();

        this.state = {
            page: 'loading',
            showPopup: false,
            action: 'init:running',
            initStatus: 'running',
            lastPage: null
        };
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.showPopup !== this.state.showPopup) {
            this.handleCustomTriggerClassUpdate();
        }
    }

    handleCustomTriggerClassUpdate() {
        const popupOpenClass = 'gh-members-popup-open';
        const popupCloseClass = 'gh-members-popup-close';
        if (this.customTriggerButton) {
            const elAddClass = this.state.showPopup ? popupOpenClass : popupCloseClass;
            const elRemoveClass = this.state.showPopup ? popupCloseClass : popupOpenClass;
            this.customTriggerButton.classList.add(elAddClass);
            this.customTriggerButton.classList.remove(elRemoveClass);
        }
    }

    getStripeUrlParam() {
        const url = new URL(window.location);
        return url.searchParams.get('stripe');
    }

    componentDidMount() {
        this.fetchData();
    }

    getDefaultPage({member = this.state.member, stripeParam} = {}) {
        // Loads default page and popup state for local UI testing
        if (process.env.NODE_ENV === 'development') {
            return {
                page: process.env.REACT_APP_DEFAULT_PAGE || 'signup',
                showPopup: true
            };
        }
        if (!member && stripeParam === 'success') {
            return {page: 'magiclink', showPopup: true};
        }
        if (member) {
            return {
                page: member.paid ? 'paidAccountHome' : 'accountHome'
            };
        }
        return {
            page: 'signup'
        };
    }

    // Fetch site and member session data with Ghost Apis
    async fetchData() {
        try {
            this.GhostApi = setupGhostApi();
            const {site, member} = await this.GhostApi.init();
            const stripeParam = this.getStripeUrlParam();
            const {page, showPopup = false} = this.getDefaultPage({member, stripeParam});
            this.setState({
                site,
                member,
                page,
                showPopup,
                action: 'init:success',
                initStatus: 'success'
            });
        } catch (e) {
            /* eslint-disable no-console */
            console.error(`[Members.js] Failed to initialize`);
            /* eslint-enable no-console */
            this.setState({
                action: 'init:failed',
                initStatus: 'failed'
            });
        }
    }

    setupCustomTriggerButton() {
        const customTriggerSelector = '[data-members-trigger-button]';
        const popupCloseClass = 'gh-members-popup-close';
        this.customTriggerButton = document.querySelector(customTriggerSelector);

        if (this.customTriggerButton) {
            const clickHandler = (event) => {
                event.preventDefault();
                this.onAction('togglePopup');
            };
            this.customTriggerButton.classList.add(popupCloseClass);
            this.customTriggerButton.addEventListener('click', clickHandler);
        }
    }

    getActionData(action) {
        const [type, status, reason] = action.split(':');
        return {type, status, reason};
    }

    getBrandColor() {
        return (this.state.site && this.state.site.brand && this.state.site.brand.primaryColor) || '#3db0ef';
    }

    async onAction(action, data) {
        this.setState({
            action: `${action}:running`
        });
        try {
            if (action === 'switchPage') {
                this.setState({
                    page: data.page,
                    lastPage: data.lastPage || null
                });
            } else if (action === 'togglePopup') {
                this.setState({
                    showPopup: !this.state.showPopup
                });
            } else if (action === 'back') {
                if (this.state.lastPage) {
                    this.setState({
                        page: this.state.lastPage
                    });
                }
            } else if (action === 'closePopup') {
                const {page: defaultPage} = this.getDefaultPage();
                this.setState({
                    showPopup: false,
                    page: this.state.page === 'magiclink' ? defaultPage : this.state.page
                });
            } else if (action === 'signout') {
                await this.GhostApi.member.signout();
                this.setState({
                    action: 'signout:success'
                });
            } else if (action === 'signin') {
                await this.GhostApi.member.sendMagicLink(data);
                this.setState({
                    action: 'signin:success',
                    page: 'magiclink'
                });
            } else if (action === 'signup') {
                const {plan, email, name} = data;
                if (plan.toLowerCase() === 'free') {
                    await this.GhostApi.member.sendMagicLink(data);
                } else {
                    await this.GhostApi.member.checkoutPlan({plan, email, name});
                }
                this.setState({
                    action: 'signup:success',
                    page: 'magiclink'
                });
            } else if (action === 'checkoutPlan') {
                const {plan} = data;
                await this.GhostApi.member.checkoutPlan({
                    plan
                });
            } else if (action === 'updatePlan') {
                const {plan, subscriptionId} = data;
                await this.GhostApi.member.updateSubscription({
                    planName: plan, subscriptionId
                });
            } else if (action === 'editBilling') {
                await this.GhostApi.member.editBilling();
            } else if (action === 'updateMember') {
                const {email, name, subscribed} = data;
                const member = await this.GhostApi.member.update({email, name, subscribed});
                if (!member) {
                    this.setState({
                        action: 'updateMember:failed'
                    });
                } else {
                    this.setState({
                        action: 'updateMember:success',
                        member: member
                    });
                }
            }
        } catch (e) {
            this.setState({
                action: `${action}:failed`
            });
        }
    }

    renderPopup() {
        if (this.state.showPopup) {
            return (
                <PopupModal />
            );
        }
        return null;
    }

    renderTriggerButton() {
        if (!this.customTriggerButton) {
            return (
                <TriggerButton
                    isPopupOpen={this.state.showPopup}
                />
            );
        }

        return null;
    }

    render() {
        if (this.state.initStatus === 'success') {
            const {site, member} = this.state;

            return (
                <ParentContext.Provider value={{
                    site,
                    member,
                    action: this.state.action,
                    brandColor: this.getBrandColor(),
                    page: this.state.page,
                    lastPage: this.state.lastPage,
                    onAction: (action, data) => this.onAction(action, data)
                }}>
                    {this.renderPopup()}
                    {this.renderTriggerButton()}
                </ParentContext.Provider>
            );
        }
        return null;
    }
}
<commit_msg>Removed env variable for default page<commit_after>import TriggerButton from './TriggerButton';
import PopupModal from './PopupModal';
import setupGhostApi from '../utils/api';
import {ParentContext} from './ParentContext';

const React = require('react');

export default class ParentContainer extends React.Component {
    constructor(props) {
        super(props);

        // Setup custom trigger button handling
        this.setupCustomTriggerButton();

        this.state = {
            page: 'loading',
            showPopup: false,
            action: 'init:running',
            initStatus: 'running',
            lastPage: null
        };
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.showPopup !== this.state.showPopup) {
            this.handleCustomTriggerClassUpdate();
        }
    }

    handleCustomTriggerClassUpdate() {
        const popupOpenClass = 'gh-members-popup-open';
        const popupCloseClass = 'gh-members-popup-close';
        if (this.customTriggerButton) {
            const elAddClass = this.state.showPopup ? popupOpenClass : popupCloseClass;
            const elRemoveClass = this.state.showPopup ? popupCloseClass : popupOpenClass;
            this.customTriggerButton.classList.add(elAddClass);
            this.customTriggerButton.classList.remove(elRemoveClass);
        }
    }

    getStripeUrlParam() {
        const url = new URL(window.location);
        return url.searchParams.get('stripe');
    }

    componentDidMount() {
        this.fetchData();
    }

    getDefaultPage({member = this.state.member, stripeParam} = {}) {
        // Loads default page and popup state for local UI testing
        if (process.env.NODE_ENV === 'development') {
            return {
                page: 'signup',
                showPopup: true
            };
        }
        if (!member && stripeParam === 'success') {
            return {page: 'magiclink', showPopup: true};
        }
        if (member) {
            return {
                page: member.paid ? 'paidAccountHome' : 'accountHome'
            };
        }
        return {
            page: 'signup'
        };
    }

    // Fetch site and member session data with Ghost Apis
    async fetchData() {
        try {
            this.GhostApi = setupGhostApi();
            const {site, member} = await this.GhostApi.init();
            const stripeParam = this.getStripeUrlParam();
            const {page, showPopup = false} = this.getDefaultPage({member, stripeParam});
            this.setState({
                site,
                member,
                page,
                showPopup,
                action: 'init:success',
                initStatus: 'success'
            });
        } catch (e) {
            /* eslint-disable no-console */
            console.error(`[Members.js] Failed to initialize`);
            /* eslint-enable no-console */
            this.setState({
                action: 'init:failed',
                initStatus: 'failed'
            });
        }
    }

    setupCustomTriggerButton() {
        const customTriggerSelector = '[data-members-trigger-button]';
        const popupCloseClass = 'gh-members-popup-close';
        this.customTriggerButton = document.querySelector(customTriggerSelector);

        if (this.customTriggerButton) {
            const clickHandler = (event) => {
                event.preventDefault();
                this.onAction('togglePopup');
            };
            this.customTriggerButton.classList.add(popupCloseClass);
            this.customTriggerButton.addEventListener('click', clickHandler);
        }
    }

    getActionData(action) {
        const [type, status, reason] = action.split(':');
        return {type, status, reason};
    }

    getBrandColor() {
        return (this.state.site && this.state.site.brand && this.state.site.brand.primaryColor) || '#3db0ef';
    }

    async onAction(action, data) {
        this.setState({
            action: `${action}:running`
        });
        try {
            if (action === 'switchPage') {
                this.setState({
                    page: data.page,
                    lastPage: data.lastPage || null
                });
            } else if (action === 'togglePopup') {
                this.setState({
                    showPopup: !this.state.showPopup
                });
            } else if (action === 'back') {
                if (this.state.lastPage) {
                    this.setState({
                        page: this.state.lastPage
                    });
                }
            } else if (action === 'closePopup') {
                const {page: defaultPage} = this.getDefaultPage();
                this.setState({
                    showPopup: false,
                    page: this.state.page === 'magiclink' ? defaultPage : this.state.page
                });
            } else if (action === 'signout') {
                await this.GhostApi.member.signout();
                this.setState({
                    action: 'signout:success'
                });
            } else if (action === 'signin') {
                await this.GhostApi.member.sendMagicLink(data);
                this.setState({
                    action: 'signin:success',
                    page: 'magiclink'
                });
            } else if (action === 'signup') {
                const {plan, email, name} = data;
                if (plan.toLowerCase() === 'free') {
                    await this.GhostApi.member.sendMagicLink(data);
                } else {
                    await this.GhostApi.member.checkoutPlan({plan, email, name});
                }
                this.setState({
                    action: 'signup:success',
                    page: 'magiclink'
                });
            } else if (action === 'checkoutPlan') {
                const {plan} = data;
                await this.GhostApi.member.checkoutPlan({
                    plan
                });
            } else if (action === 'updatePlan') {
                const {plan, subscriptionId} = data;
                await this.GhostApi.member.updateSubscription({
                    planName: plan, subscriptionId
                });
            } else if (action === 'editBilling') {
                await this.GhostApi.member.editBilling();
            } else if (action === 'updateMember') {
                const {email, name, subscribed} = data;
                const member = await this.GhostApi.member.update({email, name, subscribed});
                if (!member) {
                    this.setState({
                        action: 'updateMember:failed'
                    });
                } else {
                    this.setState({
                        action: 'updateMember:success',
                        member: member
                    });
                }
            }
        } catch (e) {
            this.setState({
                action: `${action}:failed`
            });
        }
    }

    renderPopup() {
        if (this.state.showPopup) {
            return (
                <PopupModal />
            );
        }
        return null;
    }

    renderTriggerButton() {
        if (!this.customTriggerButton) {
            return (
                <TriggerButton
                    isPopupOpen={this.state.showPopup}
                />
            );
        }

        return null;
    }

    render() {
        if (this.state.initStatus === 'success') {
            const {site, member} = this.state;

            return (
                <ParentContext.Provider value={{
                    site,
                    member,
                    action: this.state.action,
                    brandColor: this.getBrandColor(),
                    page: this.state.page,
                    lastPage: this.state.lastPage,
                    onAction: (action, data) => this.onAction(action, data)
                }}>
                    {this.renderPopup()}
                    {this.renderTriggerButton()}
                </ParentContext.Provider>
            );
        }
        return null;
    }
}
<|endoftext|>