<commit_before>///////////////////////////////////
///////    APP MANGEMENT    ///////
///////////////////////////////////

var Promise = require('promise');
var parser = require('./xmlParser.js');
var helpers;

/**
 * @class apps
 * @classdesc
 *  The apps class, has all the OC-Apps related methods.

 * Supported Methods are:
 *

 *
Apps Management
 *

 *
getApps

 *
getAttribute

 *
setAttribute

 *
deleteAttribute

 *
enableApp

 *
disableApp

 *

 *

 *

 *
 * @author Noveen Sachdeva
 * @version 1.0.0
 * @param {object}    helperFile    instance of the helpers class
 */
function apps(helperFile) {
    helpers = helperFile;
}

/**
 * Gets all enabled and non-enabled apps downloaded on the instance.
 * @returns    {Promise.<apps>}     object: {for each app: Boolean("enabled or not")}
 * @returns    {Promise.<error>}    string: error message, if any.
 */
apps.prototype.getApps = function() {
    var send = {};

    var allAppsP = helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_CLOUD, "apps");
    var allEnabledAppsP = helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_CLOUD, "apps?filter=enabled");

    return new Promise((resolve, reject) => {
        Promise.all([allAppsP, allEnabledAppsP])
            .then(apps => {
                var tree = parser.xml2js(apps[0].body);
                var statuscode = parseInt(helpers._checkOCSstatusCode(tree));
                if (statuscode === 999) {
                    reject("Provisioning API has been disabled at your instance");
                    return;
                }

                var allApps = parser.xml2js(apps[0].body).ocs.data.apps.element;
                var allEnabledApps = parser.xml2js(apps[1].body).ocs.data.apps.element;

                for (var i = 0; i < allApps.length; i++) {
                    send[allApps[i]] = false;
                }
                for (i = 0; i < allEnabledApps.length; i++) {
                    send[allEnabledApps[i]] = true;
                }

                resolve(send);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Returns an application attribute
 * @param    {string}    app     application ID (Generally app-name)
 * @param    {string}    key     attribute key or None to retrieve all values for the given application
 * @returns  {Promise.<attr>}    string: value of application's key
 * @returns  {Promise.<error>}   string: error message, if any.
 */
apps.prototype.getAttribute = function(app, key) {
    var send = "getattribute";
    if (app) {
        send += '/' + encodeURIComponent(app);

        if (key) {
            send += '/' + encodeURIComponent(helpers._encodeString(key));
        }
    }

    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_PRIVATEDATA, send)
            .then(data => {
                var elements = parser.xml2js(data.body).ocs.data.element;

                if (key) {
                    if (!elements) {
                        reject(app + ' has no key named "' + key + '"');
                    } else {
                        var value = elements.value;
                        elements.value = Object.keys(value).length === 0 && value.constructor === Object ? '' : value;
                        resolve(elements.value);
                    }
                } else {
                    if (!elements) {
                        resolve({});
                        return;
                    }
                    if (elements.constructor !== Array) {
                        elements = [elements];
                    }
                    var allAttributes = {};
                    for (var i = 0; i < elements.length; i++) {
                        allAttributes[elements[i].key] = elements[i].value;
                    }
                    resolve(allAttributes);
                }
            })
            .catch(error => {
                reject(error);
            });
    });
};

/**
 * Sets an application attribute
 * @param       {string}   app      application ID (Generally app-name)
 * @param       {string}   key      attribute key or None to retrieve all values for the given application
 * @param       {string}   value    value to set of given attribute
 * @returns     {Promise.<status>}  boolean: true if successful
 * @returns     {Promise.<error>}   string: error message, if any.
 */
apps.prototype.setAttribute = function(app, key, value) {
    var self = this;
    var path = "setattribute/" + encodeURIComponent(app) + '/' + encodeURIComponent(helpers._encodeString(key));

    /* jshint unused: false */
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_PRIVATEDATA, path, {
                'value': helpers._encodeString(value)
            })
            .then(data => {
                resolve(true);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Deletes an application attribute
 * @param       {string}    app      application ID (generally app-name)
 * @param       {string}    key      attribute key to delete for the given application
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.deleteAttribute = function(app, key) {
    var self = this;
    var path = 'deleteattribute/' + encodeURIComponent(app) + '/' + encodeURIComponent(helpers._encodeString(key));

    /* jshint unused: false */
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_PRIVATEDATA, path)
            .then(data => {
                resolve(true);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Enables an app via the Provisioning API
 * @param       {string}    app      name of the app to be enabled
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.enableApp = function(appname) {
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_CLOUD, 'apps/' + encodeURIComponent(appname))
            .then(data => {
                if (!data.body) {
                    reject("No app found by the name \"" + appname + "\"");
                }
                helpers._OCSuserResponseHandler(data, resolve, reject);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Disables an app via the Provisioning API
 * @param       {string}    app      name of the app to be disabled
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.disableApp = function(appname) {
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('DELETE', helpers.OCS_SERVICE_CLOUD, 'apps/' + encodeURIComponent(appname))
            .then(data => {
                helpers._OCSuserResponseHandler(data, resolve, reject);
            }).catch(error => {
                reject(error);
            });
    });
};

module.exports = apps;
<commit_msg>change for test<commit_after>///////////////////////////////////
///////    APP MANGEMENT    ///////
///////////////////////////////////

var Promise = require('promise');
var parser = require('./xmlParser.js');
var helpers;

/**
 * @class apps
 * @classdesc
 *  The apps class, has all the OC-Apps related methods.

 * Supported Methods are:
 *

 *
Apps Management
 *

 *
getApps

 *
getAttribute

 *
setAttribute

 *
deleteAttribute

 *
enableApp

 *
disableApp

 *

 *

 *

 *
 * @author Noveen Sachdeva
 * @version 1.0.0
 * @param {object}    helperFile    instance of the helpers class
 */
function apps(helperFile) {
    helpers = helperFile;
}

/**
 * Gets all enabled and non-enabled apps downloaded on the instance.
 * @returns    {Promise.<apps>}     object: {for each app: Boolean("enabled or not")}
 * @returns    {Promise.<error>}    string: error message, if any.
 */
apps.prototype.getApps = function() {
    var send = {};

    var allAppsP = helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_CLOUD, "apps");
    var allEnabledAppsP = helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_CLOUD, "apps?filter=enabled");

    return new Promise((resolve, reject) => {
        Promise.all([allAppsP, allEnabledAppsP])
            .then(apps => {
                var tree = parser.xml2js(apps[0].body);
                var statuscode = parseInt(helpers._checkOCSstatusCode(tree));
                if (statuscode === 999) {
                    reject("Provisioning API has been disabled at your instance");
                    return;
                }

                if ((!(data in parser.xml2js(apps[0].body).ocs)) || (!(data in parser.xml2js(apps[1].body).ocs))) {
                    reject(parser.xml2js(apps[0].body).ocs);
                }

                var allApps = parser.xml2js(apps[0].body).ocs.data.apps.element;
                var allEnabledApps = parser.xml2js(apps[1].body).ocs.data.apps.element;

                for (var i = 0; i < allApps.length; i++) {
                    send[allApps[i]] = false;
                }
                for (i = 0; i < allEnabledApps.length; i++) {
                    send[allEnabledApps[i]] = true;
                }

                resolve(send);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Returns an application attribute
 * @param    {string}    app     application ID (Generally app-name)
 * @param    {string}    key     attribute key or None to retrieve all values for the given application
 * @returns  {Promise.<attr>}    string: value of application's key
 * @returns  {Promise.<error>}   string: error message, if any.
 */
apps.prototype.getAttribute = function(app, key) {
    var send = "getattribute";
    if (app) {
        send += '/' + encodeURIComponent(app);

        if (key) {
            send += '/' + encodeURIComponent(helpers._encodeString(key));
        }
    }

    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('GET', helpers.OCS_SERVICE_PRIVATEDATA, send)
            .then(data => {
                var elements = parser.xml2js(data.body).ocs.data.element;

                if (key) {
                    if (!elements) {
                        reject(app + ' has no key named "' + key + '"');
                    } else {
                        var value = elements.value;
                        elements.value = Object.keys(value).length === 0 && value.constructor === Object ? '' : value;
                        resolve(elements.value);
                    }
                } else {
                    if (!elements) {
                        resolve({});
                        return;
                    }
                    if (elements.constructor !== Array) {
                        elements = [elements];
                    }
                    var allAttributes = {};
                    for (var i = 0; i < elements.length; i++) {
                        allAttributes[elements[i].key] = elements[i].value;
                    }
                    resolve(allAttributes);
                }
            })
            .catch(error => {
                reject(error);
            });
    });
};

/**
 * Sets an application attribute
 * @param       {string}   app      application ID (Generally app-name)
 * @param       {string}   key      attribute key or None to retrieve all values for the given application
 * @param       {string}   value    value to set of given attribute
 * @returns     {Promise.<status>}  boolean: true if successful
 * @returns     {Promise.<error>}   string: error message, if any.
 */
apps.prototype.setAttribute = function(app, key, value) {
    var self = this;
    var path = "setattribute/" + encodeURIComponent(app) + '/' + encodeURIComponent(helpers._encodeString(key));

    /* jshint unused: false */
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_PRIVATEDATA, path, {
                'value': helpers._encodeString(value)
            })
            .then(data => {
                resolve(true);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Deletes an application attribute
 * @param       {string}    app      application ID (generally app-name)
 * @param       {string}    key      attribute key to delete for the given application
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.deleteAttribute = function(app, key) {
    var self = this;
    var path = 'deleteattribute/' + encodeURIComponent(app) + '/' + encodeURIComponent(helpers._encodeString(key));

    /* jshint unused: false */
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_PRIVATEDATA, path)
            .then(data => {
                resolve(true);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Enables an app via the Provisioning API
 * @param       {string}    app      name of the app to be enabled
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.enableApp = function(appname) {
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('POST', helpers.OCS_SERVICE_CLOUD, 'apps/' + encodeURIComponent(appname))
            .then(data => {
                if (!data.body) {
                    reject("No app found by the name \"" + appname + "\"");
                }
                helpers._OCSuserResponseHandler(data, resolve, reject);
            }).catch(error => {
                reject(error);
            });
    });
};

/**
 * Disables an app via the Provisioning API
 * @param       {string}    app      name of the app to be disabled
 * @returns     {Promise.<status>}   boolean: true if successful
 * @returns     {Promise.<error>}    string: error message, if any.
 */
apps.prototype.disableApp = function(appname) {
    return new Promise((resolve, reject) => {
        helpers._makeOCSrequest('DELETE', helpers.OCS_SERVICE_CLOUD, 'apps/' + encodeURIComponent(appname))
            .then(data => {
                helpers._OCSuserResponseHandler(data, resolve, reject);
            }).catch(error => {
                reject(error);
            });
    });
};

module.exports = apps;
<|endoftext|>


<commit_before>import knot from 'knot.js'
import Wait from 'wait.js'

export default class Spritz {

    /**
        --- CORE ---
    **/

    constructor (selector, options = {}) {
    // instance constructor
        this.options = {
            picture: options.picture || [],
            steps: options.steps || 1,
            rows: options.rows || 1
        }

        this.selector = typeof selector === 'string'
            ? document.querySelector(selector)
            : selector

        this.emitter = knot()
        this.waitter = new Wait()
        this.supportsWebP = this._supportsWebP()

        this.initiated = false

        return this
    }

    _globalVars () {
    // global vars
        this.canvas = false
        this.ctx = false
        this.loaded = false
        this._resetUntil()
        this.anim = false
        this.columns = this.options.steps / this.options.rows
        this.currentFps = 15
        this.flipped = false
    }

    _throttle (callback, delay) {
    // throttle function
        let last
        let timer

        return () => {
            let context = this
            let now = +new Date()
            let args = arguments

            if (last && now < last + delay) {
                clearTimeout(timer)
                timer = setTimeout(() => {
                    last = now
                    callback.apply(context, args)
                }, delay)
            } else {
                last = now
                callback.apply(context, args)
            }
        }
    }

    _bindEvents () {
    // create events listeners
        this.resize = this._throttle((event) => {
            this._resize()
        }, 250)

        window.addEventListener('resize', this.resize, false)
    }

    _unbindEvents () {
    // remove events listeners
        window.removeEventListener('resize', this.resize, false)
    }

    _resize () {
    // viewport resize triggered
        this._loadPicture()
        this.emitter.emit('resize', this.pic)
    }

    /**
        --- API ---
    **/

    init (step = 1) {
    // init the sprite
        if (!this.initiated) {
            this.initialStep = step
            this.currentStep = step

            this._globalVars()
            this._bindEvents()
            this._createCanvas()
            this._loadPicture()

            this.initiated = true
            this.emitter.emit('ready')
        }

        return this
    }

    destroy () {
    // destroy sprite instance
        this.waitter.handle(() => {
            if (this.initiated) {
                // stop stuff
                this.stop()
                this._unbindEvents()

                // reset & remove canvas
                this.canvas.parentNode.removeChild(this.canvas)
                this.container.parentNode.removeChild(this.container)
                this.canvas = false
                this.ctx = false

                // turn initiated to false
                this.initiated = false

                // emitt destroy
                this.emitter.emit('destroy')

                // turn off emitters
                this.emitter.off('ready')
                this.emitter.off('destroy')
                this.emitter.off('resize')
                this.emitter.off('play')
                this.emitter.off('load')
                this.emitter.off('change')
                this.emitter.off('wait')
                this.emitter.off('flip')
                this.emitter.off('pause')
                this.emitter.off('stop')
            }
        })

        return this
    }

    play (direction = false) {
    // play animation forward
        this.waitter.handle(() => {
            this.animDirection = (direction === 'backward') ? 'backward' : 'forward'
            this._startAnimation()

            this.emitter.emit('play', this.animDirection)
        })

        return this
    }

    playback () {
    // play animation backward
        this.waitter.handle(() => {
            this.animDirection = 'backward'
            this._startAnimation()

            this.emitter.emit('play', this.animDirection)
        })

        return this
    }

    pause (silent = false) {
    // pause animation
        this.waitter.handle(() => {
            this._pauseAnimation()

            if (!silent) {
                this.emitter.emit('pause')
            }
        })

        return this
    }

    stop () {
    // stop animation (= pause + reset)
        this.waitter.handle(() => {
            this.pause(true)
            this.step(this.initialStep)

            this.emitter.emit('stop')
        })

        return this
    }

    wait (milliseconds = 0) {
    // chainable timeout
        this.waitter.handle(() => {
            this.emitter.emit('wait', milliseconds)
        }, milliseconds)

        return this
    }

    step (step = 1) {
    // change the current frame/step
        this.waitter.handle(() => {
            let fromStep = this.currentStep
            this.currentStep = step
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    fps (speed) {
    // change animation speed
        this.waitter.handle(() => {
            this.currentFps = speed
        })

        return this
    }

    until (step, loop = 1) {
    // next animation will stop at this
        this.waitter.handle(() => {
            this.stopAtStep = step
            this.stopAtLoop = loop
        })

        return this
    }

    next () {
    // go to the next frame
        this.waitter.handle(() => {
            this.animDirection = 'forward'
            let fromStep = this.currentStep
            this.currentStep = this._targetStep()
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    prev () {
    // go to the previous frame
        this.waitter.handle(() => {
            this.animDirection = 'backward'
            let fromStep = this.currentStep
            this.currentStep = this._targetStep()
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    get (data, callback = false) {
    // return data, then call the callback function with result
        this.waitter.handle(() => {
            switch (data) {
                case 'step':
                    return callback !== false ? callback.call(this, this.currentStep) : this.currentStep
                case 'picture':
                    return callback !== false ? callback.call(this, this.pic) : this.pic
                default:
                    return false
            }
        })

        return this
    }

    flip () {
    // flip the sprite horizontally
        this.waitter.handle(() => {
            let css = this.flipped
                ? 'width:100%;height:100%;'
                : 'width:100%;height:100%;-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1);-webkit-filter:FlipH;filter:FlipH;'

            this.container.setAttribute('style', css)
            this.flipped = !this.flipped
            this.emitter.emit('flip')
        })

        return this
    }

    on (...args) { return this.emitter.on(...args) }
    off (...args) { return this.emitter.off(...args) }
    once (...args) { return this.emitter.once(...args) }

    /**
        --- ANIMATE ---
    **/

    _resetUntil () {
    // reset the "until()" api command
        this.stopAtLoop = false
        this.stopAtStep = false
    }

    _targetStep () {
    // return the following step to be displayed
        if (this.animDirection === 'forward') {
            return this.currentStep < this.options.steps
                ? this.currentStep + 1
                : 1
        } else {
            return this.currentStep > 1
                ? this.currentStep - 1
                : this.options.steps
        }
    }

    _animate (timestamp) {
    // frame animation
        if (this.animTime === undefined) {
            this.animTime = timestamp
        }

        let seg = Math.floor((timestamp - this.animTime) / (1000 / this.currentFps))
        let pauseAnim = false

        if (seg > this.animFrame) {
            this.animFrame = seg
            this.currentStep = this._targetStep()

            let draw = true
            if (this.currentStep === this.stopAtStep) {
                this.currentLoop ++
                if (this.currentLoop === this.stopAtLoop) {
                    draw = false
                }
            }

            if (draw) {
                this._draw()
            } else {
                this.pause()
                pauseAnim = true
            }
        }

        if (!pauseAnim) {
            this.anim = window.requestAnimationFrame((timestamp) => this._animate(timestamp))
        }
    }

    _startAnimation () {
    // start animation
        if (!this.anim) {
            this.animTime = undefined
            this.animFrame = -1
            this.currentLoop = 0
            this.anim = window.requestAnimationFrame((timestamp) => this._animate(timestamp))
        }
    }

    _pauseAnimation () {
    // pause animation
        if (this.anim) {
            this._resetUntil()
            window.cancelAnimationFrame(this.anim)
            this.anim = false
        }
    }

    /**
        --- DETECT & CALCULATE ---
    **/

    _selectPicture () {
    // select picture src from list
        for (let i = 0; i < this.options.picture.length; i++) {
            let pic = this.options.picture[i]
            if (this._supportsFormat(pic.srcset) && this._matchesMedia(pic.media)) {
                this.pic = pic
                return pic.srcset
            }
        }
        return false
    }

    _supportsFormat (filename) {
    // return true if filename is a supported format
        let ext = this._getExtension(filename)
        return (ext === 'webp' && this.supportsWebP) || ext !== 'webp'
    }

    _supportsWebP () {
    // return true if webP is supported
        let canvas = document.createElement('canvas')
        canvas.width = canvas.height = 1
        return canvas.toDataURL && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0
    }

    _getExtension (filename) {
    // return the filename extension
        return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined
    }

    _matchesMedia (query = undefined) {
    // return true if matches the media query
        let mq = window.matchMedia(query)
        return query !== undefined ? mq.matches : true
    }

    _setDimensions () {
    // calculate sprite dimensions
        this.stepWidth = this.pic.width / this.columns
        this.stepHeight = this.pic.height / this.options.rows
        this.stepRatio = this.stepWidth / this.stepHeight

        this.parentWidth = this.selector.clientWidth
        this.parentHeight = this.selector.clientHeight
        this.parentRatio = this.parentWidth / this.parentHeight

        if (this.stepRatio >= this.parentRatio) {
            this.canvasWidth = this.parentWidth
            this.canvasHeight = (this.stepHeight * this.canvasWidth) / this.stepWidth
        } else {
            this.canvasHeight = this.parentHeight
            this.canvasWidth = (this.stepWidth * this.canvasHeight) / this.stepHeight
        }

        this.canvas.width = this.canvasWidth
        this.canvas.height = this.canvasHeight
    }

    /**
        --- CREATE & DRAW ---
    **/

    _loadPicture () {
    // load source picture
        this.picture = new Image()
        this.picture.onload = () => {
            if (!this.loaded) {
                this.emitter.emit('load', this.pic)
                this.loaded = true
            }
            this._draw()
        }
        this.picture.src = this._selectPicture()
    }

    _draw () {
    // draw sprite
        this._setDimensions()
        this._drawPicture()
    }

    _drawPicture () {
    // draw picture into canvas
        let targetColumn = (this.currentStep - 1) % this.columns
        let targetRow = Math.floor((this.currentStep - 1) / this.columns)

        let posX = targetColumn * this.stepWidth
        let posY = targetRow * this.stepHeight

        this.ctx.drawImage(
            this.picture,
            posX, posY,
            this.stepWidth,
            this.stepHeight,
            0, 0,
            this.canvasWidth,
            this.canvasHeight
        )
    }

    _createCanvas () {
    // create html5 canvas
        this.canvas = document.createElement('canvas')
        this.canvas.setAttribute('style', 'position:absolute;left:50%;top:50%;z-index:1;-webkit-transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);-ms-transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);')

        this.container = document.createElement('div')
        this.container.setAttribute('style', 'width:100%;height:100%;')
        this.container.appendChild(this.canvas)

        this.selector.appendChild(this.container)
        this.ctx = this.canvas.getContext('2d')
    }

}
<commit_msg>DrawImage position updated<commit_after>import knot from 'knot.js'
import Wait from 'wait.js'

export default class Spritz {

    /**
        --- CORE ---
    **/

    constructor (selector, options = {}) {
    // instance constructor
        this.options = {
            picture: options.picture || [],
            steps: options.steps || 1,
            rows: options.rows || 1
        }

        this.selector = typeof selector === 'string'
            ? document.querySelector(selector)
            : selector

        this.emitter = knot()
        this.waitter = new Wait()
        this.supportsWebP = this._supportsWebP()

        this.initiated = false

        return this
    }

    _globalVars () {
    // global vars
        this.canvas = false
        this.ctx = false
        this.loaded = false
        this._resetUntil()
        this.anim = false
        this.columns = this.options.steps / this.options.rows
        this.currentFps = 15
        this.flipped = false
    }

    _throttle (callback, delay) {
    // throttle function
        let last
        let timer

        return () => {
            let context = this
            let now = +new Date()
            let args = arguments

            if (last && now < last + delay) {
                clearTimeout(timer)
                timer = setTimeout(() => {
                    last = now
                    callback.apply(context, args)
                }, delay)
            } else {
                last = now
                callback.apply(context, args)
            }
        }
    }

    _bindEvents () {
    // create events listeners
        this.resize = this._throttle((event) => {
            this._resize()
        }, 250)

        window.addEventListener('resize', this.resize, false)
    }

    _unbindEvents () {
    // remove events listeners
        window.removeEventListener('resize', this.resize, false)
    }

    _resize () {
    // viewport resize triggered
        this._loadPicture()
        this.emitter.emit('resize', this.pic)
    }

    /**
        --- API ---
    **/

    init (step = 1) {
    // init the sprite
        if (!this.initiated) {
            this.initialStep = step
            this.currentStep = step

            this._globalVars()
            this._bindEvents()
            this._createCanvas()
            this._loadPicture()

            this.initiated = true
            this.emitter.emit('ready')
        }

        return this
    }

    destroy () {
    // destroy sprite instance
        this.waitter.handle(() => {
            if (this.initiated) {
                // stop stuff
                this.stop()
                this._unbindEvents()

                // reset & remove canvas
                this.canvas.parentNode.removeChild(this.canvas)
                this.container.parentNode.removeChild(this.container)
                this.canvas = false
                this.ctx = false

                // turn initiated to false
                this.initiated = false

                // emitt destroy
                this.emitter.emit('destroy')

                // turn off emitters
                this.emitter.off('ready')
                this.emitter.off('destroy')
                this.emitter.off('resize')
                this.emitter.off('play')
                this.emitter.off('load')
                this.emitter.off('change')
                this.emitter.off('wait')
                this.emitter.off('flip')
                this.emitter.off('pause')
                this.emitter.off('stop')
            }
        })

        return this
    }

    play (direction = false) {
    // play animation forward
        this.waitter.handle(() => {
            this.animDirection = (direction === 'backward') ? 'backward' : 'forward'
            this._startAnimation()

            this.emitter.emit('play', this.animDirection)
        })

        return this
    }

    playback () {
    // play animation backward
        this.waitter.handle(() => {
            this.animDirection = 'backward'
            this._startAnimation()

            this.emitter.emit('play', this.animDirection)
        })

        return this
    }

    pause (silent = false) {
    // pause animation
        this.waitter.handle(() => {
            this._pauseAnimation()

            if (!silent) {
                this.emitter.emit('pause')
            }
        })

        return this
    }

    stop () {
    // stop animation (= pause + reset)
        this.waitter.handle(() => {
            this.pause(true)
            this.step(this.initialStep)

            this.emitter.emit('stop')
        })

        return this
    }

    wait (milliseconds = 0) {
    // chainable timeout
        this.waitter.handle(() => {
            this.emitter.emit('wait', milliseconds)
        }, milliseconds)

        return this
    }

    step (step = 1) {
    // change the current frame/step
        this.waitter.handle(() => {
            let fromStep = this.currentStep
            this.currentStep = step
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    fps (speed) {
    // change animation speed
        this.waitter.handle(() => {
            this.currentFps = speed
        })

        return this
    }

    until (step, loop = 1) {
    // next animation will stop at this
        this.waitter.handle(() => {
            this.stopAtStep = step
            this.stopAtLoop = loop
        })

        return this
    }

    next () {
    // go to the next frame
        this.waitter.handle(() => {
            this.animDirection = 'forward'
            let fromStep = this.currentStep
            this.currentStep = this._targetStep()
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    prev () {
    // go to the previous frame
        this.waitter.handle(() => {
            this.animDirection = 'backward'
            let fromStep = this.currentStep
            this.currentStep = this._targetStep()
            this._draw()

            this.emitter.emit('change', fromStep, this.currentStep)
        })

        return this
    }

    get (data, callback = false) {
    // return data, then call the callback function with result
        this.waitter.handle(() => {
            switch (data) {
                case 'step':
                    return callback !== false ? callback.call(this, this.currentStep) : this.currentStep
                case 'picture':
                    return callback !== false ? callback.call(this, this.pic) : this.pic
                default:
                    return false
            }
        })

        return this
    }

    flip () {
    // flip the sprite horizontally
        this.waitter.handle(() => {
            let css = this.flipped
                ? 'width:100%;height:100%;'
                : 'width:100%;height:100%;-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1);-webkit-filter:FlipH;filter:FlipH;'

            this.container.setAttribute('style', css)
            this.flipped = !this.flipped
            this.emitter.emit('flip')
        })

        return this
    }

    on (...args) { return this.emitter.on(...args) }
    off (...args) { return this.emitter.off(...args) }
    once (...args) { return this.emitter.once(...args) }

    /**
        --- ANIMATE ---
    **/

    _resetUntil () {
    // reset the "until()" api command
        this.stopAtLoop = false
        this.stopAtStep = false
    }

    _targetStep () {
    // return the following step to be displayed
        if (this.animDirection === 'forward') {
            return this.currentStep < this.options.steps
                ? this.currentStep + 1
                : 1
        } else {
            return this.currentStep > 1
                ? this.currentStep - 1
                : this.options.steps
        }
    }

    _animate (timestamp) {
    // frame animation
        if (this.animTime === undefined) {
            this.animTime = timestamp
        }

        let seg = Math.floor((timestamp - this.animTime) / (1000 / this.currentFps))
        let pauseAnim = false

        if (seg > this.animFrame) {
            this.animFrame = seg
            this.currentStep = this._targetStep()

            let draw = true
            if (this.currentStep === this.stopAtStep) {
                this.currentLoop ++
                if (this.currentLoop === this.stopAtLoop) {
                    draw = false
                }
            }

            if (draw) {
                this._draw()
            } else {
                this.pause()
                pauseAnim = true
            }
        }

        if (!pauseAnim) {
            this.anim = window.requestAnimationFrame((timestamp) => this._animate(timestamp))
        }
    }

    _startAnimation () {
    // start animation
        if (!this.anim) {
            this.animTime = undefined
            this.animFrame = -1
            this.currentLoop = 0
            this.anim = window.requestAnimationFrame((timestamp) => this._animate(timestamp))
        }
    }

    _pauseAnimation () {
    // pause animation
        if (this.anim) {
            this._resetUntil()
            window.cancelAnimationFrame(this.anim)
            this.anim = false
        }
    }

    /**
        --- DETECT & CALCULATE ---
    **/

    _selectPicture () {
    // select picture src from list
        for (let i = 0; i < this.options.picture.length; i++) {
            let pic = this.options.picture[i]
            if (this._supportsFormat(pic.srcset) && this._matchesMedia(pic.media)) {
                this.pic = pic
                return pic.srcset
            }
        }
        return false
    }

    _supportsFormat (filename) {
    // return true if filename is a supported format
        let ext = this._getExtension(filename)
        return (ext === 'webp' && this.supportsWebP) || ext !== 'webp'
    }

    _supportsWebP () {
    // return true if webP is supported
        let canvas = document.createElement('canvas')
        canvas.width = canvas.height = 1
        return canvas.toDataURL && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0
    }

    _getExtension (filename) {
    // return the filename extension
        return (/[.]/.exec(filename)) ? /[^.]+$/.exec(filename)[0] : undefined
    }

    _matchesMedia (query = undefined) {
    // return true if matches the media query
        let mq = window.matchMedia(query)
        return query !== undefined ? mq.matches : true
    }

    _setDimensions () {
    // calculate sprite dimensions
        this.stepWidth = this.pic.width / this.columns
        this.stepHeight = this.pic.height / this.options.rows
        this.stepRatio = this.stepWidth / this.stepHeight

        this.parentWidth = this.selector.clientWidth
        this.parentHeight = this.selector.clientHeight
        this.parentRatio = this.parentWidth / this.parentHeight

        if (this.stepRatio >= this.parentRatio) {
            this.canvasWidth = this.parentWidth
            this.canvasHeight = (this.stepHeight * this.canvasWidth) / this.stepWidth
        } else {
            this.canvasHeight = this.parentHeight
            this.canvasWidth = (this.stepWidth * this.canvasHeight) / this.stepHeight
        }

        this.canvas.width = this.canvasWidth
        this.canvas.height = this.canvasHeight
    }

    /**
        --- CREATE & DRAW ---
    **/

    _loadPicture () {
    // load source picture
        this.picture = new Image()
        this.picture.onload = () => {
            if (!this.loaded) {
                this.emitter.emit('load', this.pic)
                this.loaded = true
            }
            this._draw()
        }
        this.picture.src = this._selectPicture()
    }

    _draw () {
    // draw sprite
        this._setDimensions()
        this._drawPicture()
    }

    _drawPicture () {
    // draw picture into canvas
        let targetColumn = (this.currentStep - 1) % this.columns
        let targetRow = Math.floor((this.currentStep - 1) / this.columns)

        let posX = targetColumn * this.stepWidth
        let posY = targetRow * this.stepHeight

        this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)

        this.ctx.drawImage(
            this.picture,
            Math.round(posX) + 0.5, Math.round(posY) + 0.5,
            this.stepWidth,
            this.stepHeight,
            0, 0,
            this.canvasWidth,
            this.canvasHeight
        )
    }

    _createCanvas () {
    // create html5 canvas
        this.canvas = document.createElement('canvas')
        this.canvas.setAttribute('style', 'position:absolute;left:50%;top:50%;z-index:1;-webkit-transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);-ms-transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);transform:translateY(-50%) translateY(1px) translateX(-50%) translateX(1px);')

        this.container = document.createElement('div')
        this.container.setAttribute('style', 'width:100%;height:100%;')
        this.container.appendChild(this.canvas)

        this.selector.appendChild(this.container)
        this.ctx = this.canvas.getContext('2d')
    }

}
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const router = express.Router({ mergeParams: true })

const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs'))
const path = require('path')
const jsYaml = require('js-yaml')

const Git = require('nodegit')

router.get('/', (req, res) => {
  res.json({
    owner: req.params.owner,
    repo: req.params.repo
  })
})

function ensureDirectoryExists (fullPath) {
  return fs.mkdirAsync(fullPath)
    .then(() => fullPath)
    .catch((err) => {
      if (err.code === 'EEXIST') {
        return fullPath
      }
      throw err
    })
}

function ensureHomeDirectoryExists (repository) {
  return ensureDirectoryExists('/opt/ludwig')
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.provider)))
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.owner)))
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.name)))
}

function getRepositoryURL (repository) {
  return 'https://github.com/' + repository.owner + '/' + repository.name + '.git'
}

function fetchRepo (repo) {
  return repo.fetch('origin', {
    updateFetchhead: 1
  }).then(() => repo)
}

function checkoutMaster (repo) {
  return repo.checkoutBranch('origin/master').then(() => repo)
}

function filter (directory) {
  return Promise.all(fs.readdirAsync(directory)
    .filter((file) => {
      const fullPath = path.join(directory, file)
      return fs.statAsync(fullPath)
        .then((stats) => stats.isFile())
        .catch(() => false)
    })
  )
}

function serialize (testFile) {
  return fs.readFileAsync(testFile.fullPath, { encoding: 'utf-8' })
    .then((content) => {
      const ext = path.extname(testFile.fullPath)
      let result = {
        id: testFile.id
      }
      if (ext === '.yaml') {
        result.data = jsYaml.safeLoad(content)
      } else {
        result.content = content
      }
      return result
    })
    .catch((err) => {
      console.error(testFile.fullPath, err.message)
      throw err
    })
}

function main (repository) {
  return ensureHomeDirectoryExists(repository)
    .then((repositoryFullPath) => {
      const fullPath = path.join(repositoryFullPath, 'content')
      return Git.Clone(getRepositoryURL(repository), fullPath)
        .catch((err) => {
          if (err.message.match(/exists and is not an empty directory/)) {
            return Git.Repository.open(fullPath)
          }

          throw err
        })
    })
    .then(fetchRepo)
    .then(checkoutMaster)
    .then((ref) => {
      const folder = path.join(ref.workdir(), repository.folder || 'tests')
      const testPath = path.resolve(path.join(ref.workdir(), '../tests.json'))
      return filter(folder)
        .then((files) => {
          return Promise.map(files, (file) => {
            return {
              id: file,
              fullPath: path.join(folder, file)
            }
          }).map(serialize)
        })
        .then(JSON.stringify)
        .then((files) => fs.writeFile(testPath, files, { encoding: 'utf-8' }))
        .then(() => ref)
    })
    .then(() => repository)
}

router.use((req, res, next) => {
  const repo = {
    provider: 'github',
    owner: req.params.owner,
    name: req.params.repo
  }
  const id = [repo.provider, repo.owner, repo.name].join('/')
  const repoList = require('../../list')

  var details = repoList.find(function (item) { return item.id === id })
  if (!details) {
    return res.status(404).json(repo)
  }
  req.repository = Object.assign(repo, details)
  next()
})

router.get('/tests', (req, res) => {
  ensureHomeDirectoryExists(req.repository)
    .then((homeDirectory) => res.sendFile(path.join(homeDirectory, 'tests.json')))
})

router.get('/refresh', (req, res) => {
  const start = new Date()
  main(req.repository)
    .then(repo => {
      res.json({
        refresh: (new Date()).getTime() - start.getTime(),
        repository: repo
      })
    })
    .catch(function (err) {
      res.status(500).json(JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err))))
    })
})

module.exports = router
<commit_msg>Make sure API endpoint are accessible from everywhere<commit_after>'use strict'

const express = require('express')
const router = express.Router({ mergeParams: true })

const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs'))
const path = require('path')
const jsYaml = require('js-yaml')

const Git = require('nodegit')

router.get('/', (req, res) => {
  res.json({
    owner: req.params.owner,
    repo: req.params.repo
  })
})

function ensureDirectoryExists (fullPath) {
  return fs.mkdirAsync(fullPath)
    .then(() => fullPath)
    .catch((err) => {
      if (err.code === 'EEXIST') {
        return fullPath
      }
      throw err
    })
}

function ensureHomeDirectoryExists (repository) {
  return ensureDirectoryExists('/opt/ludwig')
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.provider)))
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.owner)))
    .then((parentFullPath) => ensureDirectoryExists(path.join(parentFullPath, repository.name)))
}

function getRepositoryURL (repository) {
  return 'https://github.com/' + repository.owner + '/' + repository.name + '.git'
}

function fetchRepo (repo) {
  return repo.fetch('origin', {
    updateFetchhead: 1
  }).then(() => repo)
}

function checkoutMaster (repo) {
  return repo.checkoutBranch('origin/master').then(() => repo)
}

function filter (directory) {
  return Promise.all(fs.readdirAsync(directory)
    .filter((file) => {
      const fullPath = path.join(directory, file)
      return fs.statAsync(fullPath)
        .then((stats) => stats.isFile())
        .catch(() => false)
    })
  )
}

function serialize (testFile) {
  return fs.readFileAsync(testFile.fullPath, { encoding: 'utf-8' })
    .then((content) => {
      const ext = path.extname(testFile.fullPath)
      let result = {
        id: testFile.id
      }
      if (ext === '.yaml') {
        result.data = jsYaml.safeLoad(content)
      } else {
        result.content = content
      }
      return result
    })
    .catch((err) => {
      console.error(testFile.fullPath, err.message)
      throw err
    })
}

function main (repository) {
  return ensureHomeDirectoryExists(repository)
    .then((repositoryFullPath) => {
      const fullPath = path.join(repositoryFullPath, 'content')
      return Git.Clone(getRepositoryURL(repository), fullPath)
        .catch((err) => {
          if (err.message.match(/exists and is not an empty directory/)) {
            return Git.Repository.open(fullPath)
          }

          throw err
        })
    })
    .then(fetchRepo)
    .then(checkoutMaster)
    .then((ref) => {
      const folder = path.join(ref.workdir(), repository.folder || 'tests')
      const testPath = path.resolve(path.join(ref.workdir(), '../tests.json'))
      return filter(folder)
        .then((files) => {
          return Promise.map(files, (file) => {
            return {
              id: file,
              fullPath: path.join(folder, file)
            }
          }).map(serialize)
        })
        .then(JSON.stringify)
        .then((files) => fs.writeFile(testPath, files, { encoding: 'utf-8' }))
        .then(() => ref)
    })
    .then(() => repository)
}

router.use((req, res, next) => {
  const repo = {
    provider: 'github',
    owner: req.params.owner,
    name: req.params.repo
  }
  const id = [repo.provider, repo.owner, repo.name].join('/')
  const repoList = require('../../list')

  var details = repoList.find(function (item) { return item.id === id })
  if (!details) {
    return res.status(404).json(repo)
  }
  req.repository = Object.assign(repo, details)
  next()
})

router.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
  next()
})

router.get('/tests', (req, res) => {
  ensureHomeDirectoryExists(req.repository)
    .then((homeDirectory) => res.sendFile(path.join(homeDirectory, 'tests.json')))
})

router.get('/refresh', (req, res) => {
  const start = new Date()
  main(req.repository)
    .then(repo => {
      res.json({
        refresh: (new Date()).getTime() - start.getTime(),
        repository: repo
      })
    })
    .catch(function (err) {
      res.status(500).json(JSON.parse(JSON.stringify(err, Object.getOwnPropertyNames(err))))
    })
})

module.exports = router
<|endoftext|>


<commit_before><commit_msg>Remove duplicate controller<commit_after><|endoftext|>


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

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, logCommits, generateChangelog } = require('./lib/git');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && execa.shell('mkdir dist');
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            enabled: () => !isCI,
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'ci';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return logCommits(branch, flowType).then((data) => {
            if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
                const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
                coucou.send(data, { env, flowType }, PKG);
            }
        });
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const { branch, flow: flowType, custom } = argv;
    debug(argv, 'arguments');
    return logCommits(branch, flowType).then((data) => {
        if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
            const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
            coucou.send(data, { env, flowType, custom }, PKG);
        }
    });
}

if (argv._.includes('changelog')) {
    const { branch, url = PKG.bugs.url } = argv;
    debug({ argv }, 'arguments');

    if (!['dev', process.env.QA_BRANCH].includes(branch)) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    return generateChangelog(branch, url).then((data) => {
        if (data) {
            coucou.send(data, { env: branch, mode: 'changelog' }, PKG);
        }
    });
}

main().catch(error);
<commit_msg>:warning: if no URL for the changelog<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, logCommits, generateChangelog } = require('./lib/git');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && execa.shell('mkdir dist');
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            enabled: () => !isCI,
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'ci';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return logCommits(branch, flowType).then((data) => {
            if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
                const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
                coucou.send(data, { env, flowType }, PKG);
            }
        });
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const { branch, flow: flowType, custom } = argv;
    debug(argv, 'arguments');
    return logCommits(branch, flowType).then((data) => {
        if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
            const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
            coucou.send(data, { env, flowType, custom }, PKG);
        }
    });
}

if (argv._.includes('changelog')) {
    const { branch, url = (PKG.bugs || {}).url } = argv;
    debug({ argv }, 'arguments');

    if (!['dev', process.env.QA_BRANCH].includes(branch)) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    if (!url) {
        return warn('No URL found for the issues');
    }

    return generateChangelog(branch, url).then((data) => {
        if (data) {
            coucou.send(data, { env: branch, mode: 'changelog' }, PKG);
        }
    });
}

main().catch(error);
<|endoftext|>


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

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        website: false,
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, logCommits, generateChangelog } = require('./lib/git');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && execa('mkdir dist');
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            enabled: () => !isCI,
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        {
            title: 'Generate the version info',
            task(ctx) {
                const { tag = `v${PKG.version}`, commit } = ctx.config;
                const fileName = path.join('dist', 'assets/version.json');
                return script('createVersionJSON.sh', [commit, tag, fileName]);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'ci';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return logCommits(branch, flowType).then((data) => {
            if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
                const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
                coucou.send(data, { env, flowType }, PKG);
            }
        });
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const parseEnv = (branch, website) => {
        if (website && /deploy-(a|b)$/.test(branch)) {
            return 'deploy-prod';
        }
        return branch;
    };

    const { branch, flow: flowType, custom } = argv;
    debug(argv, 'arguments');
    const branchName = parseEnv(branch, argv.website);
    return logCommits(branchName, flowType, argv.website).then((data) => {
        if (/deploy-(beta|prod|old|tor|dev)/.test(branchName)) {
            const [, env] = branchName.match(/deploy-(beta|prod|old|tor|dev)/);
            coucou.send(data, { env, flowType, custom }, PKG);
        }
    });
}

if (argv._.includes('changelog')) {
    const { branch } = argv;
    const url = argv.url || (PKG.bugs || {}).url;
    debug({ argv }, 'arguments');

    if (!['dev', 'v4', process.env.QA_BRANCH].includes(branch)) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    if (!url) {
        return warn('No URL found for the issues');
    }

    return generateChangelog(branch, url).then((data) => {
        if (data) {
            coucou.send(data, { env: branch, mode: 'changelog' }, PKG);
        }
    });
}

main().catch(error);
<commit_msg>Context can be undeefined<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        website: false,
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, logCommits, generateChangelog } = require('./lib/git');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && execa('mkdir dist');
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            enabled: () => !isCI,
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        {
            title: 'Generate the version info',
            task(ctx) {
                const { tag = `v${PKG.version}`, commit } = ctx.config || {};
                const fileName = path.join('dist', 'assets/version.json');
                return script('createVersionJSON.sh', [commit, tag, fileName]);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'ci';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return logCommits(branch, flowType).then((data) => {
            if (/deploy-(beta|prod|old|tor|dev)/.test(branch)) {
                const [, env] = branch.match(/deploy-(beta|prod|old|tor|dev)/);
                coucou.send(data, { env, flowType }, PKG);
            }
        });
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const parseEnv = (branch, website) => {
        if (website && /deploy-(a|b)$/.test(branch)) {
            return 'deploy-prod';
        }
        return branch;
    };

    const { branch, flow: flowType, custom } = argv;
    debug(argv, 'arguments');
    const branchName = parseEnv(branch, argv.website);
    return logCommits(branchName, flowType, argv.website).then((data) => {
        if (/deploy-(beta|prod|old|tor|dev)/.test(branchName)) {
            const [, env] = branchName.match(/deploy-(beta|prod|old|tor|dev)/);
            coucou.send(data, { env, flowType, custom }, PKG);
        }
    });
}

if (argv._.includes('changelog')) {
    const { branch } = argv;
    const url = argv.url || (PKG.bugs || {}).url;
    debug({ argv }, 'arguments');

    if (!['dev', 'v4', process.env.QA_BRANCH].includes(branch)) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    if (!url) {
        return warn('No URL found for the issues');
    }

    return generateChangelog(branch, url).then((data) => {
        if (data) {
            coucou.send(data, { env: branch, mode: 'changelog' }, PKG);
        }
    });
}

main().catch(error);
<|endoftext|>


<commit_before>var bcrypto = require('./crypto')
var bs58check = require('bs58check')
var ecdsa = require('./ecdsa')
var ecurve = require('ecurve')
var randomBytes = require('randombytes')
var typeforce = require('typeforce')
var types = require('./types')

var NETWORKS = require('./networks')
var BigInteger = require('bigi')

var secp256k1 = ecurve.getCurveByName('secp256k1')

function ECPair (d, Q, options) {
  if (options) {
    typeforce({
      compressed: types.maybe(types.Boolean),
      network: types.maybe(types.Network)
    }, options)
  }

  options = options || {}

  if (d) {
    if (d.signum() <= 0) throw new Error('Private key must be greater than 0')
    if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order')
    if (Q) throw new TypeError('Unexpected publicKey parameter')

    this.d = d

  } else {
    typeforce(types.ECPoint, Q)

    this.__Q = Q
  }

  this.compressed = options.compressed === undefined ? true : options.compressed
  this.network = options.network || NETWORKS.bitcoin
}

Object.defineProperty(ECPair.prototype, 'Q', {
  get: function () {
    if (!this.__Q && this.d) {
      this.__Q = secp256k1.G.multiply(this.d)
    }

    return this.__Q
  }
})

ECPair.fromPublicKeyBuffer = function (buffer, network) {
  var Q = ecurve.Point.decodeFrom(secp256k1, buffer)

  return new ECPair(null, Q, {
    compressed: Q.compressed,
    network: network
  })
}

ECPair.fromWIF = function (string, networks) {
  var payload = bs58check.decode(string)
  var version = payload.readUInt8(0)
  var compressed

  if (payload.length === 34) {
    if (payload[33] !== 0x01) throw new Error('Invalid compression flag')

    // truncate the version/compression bytes
    payload = payload.slice(1, -1)
    compressed = true

  // no compression flag
  } else {
    if (payload.length !== 33) throw new Error('Invalid WIF payload length')

    // Truncate the version byte
    payload = payload.slice(1)
    compressed = false
  }

  var network

  // list of networks?
  if (Array.isArray(networks)) {
    network = networks.filter(function (network) {
      return version === network.wif
    }).pop() || {}

  // otherwise, assume a network object (or default to bitcoin)
  } else {
    network = networks || NETWORKS.bitcoin
  }

  if (version !== network.wif) throw new Error('Invalid network')

  var d = BigInteger.fromBuffer(payload)

  return new ECPair(d, null, {
    compressed: compressed,
    network: network
  })
}

ECPair.makeRandom = function (options) {
  options = options || {}

  var rng = options.rng || randomBytes

  var d
  do {
    var buffer = rng(32)
    typeforce(types.Buffer256bit, buffer)

    d = BigInteger.fromBuffer(buffer)
  } while (d.compareTo(secp256k1.n) > 0)

  return new ECPair(d, null, options)
}

ECPair.prototype.toWIF = function () {
  if (!this.d) throw new Error('Missing private key')

  var bufferLen = this.compressed ? 34 : 33
  var buffer = new Buffer(bufferLen)

  buffer.writeUInt8(this.network.wif, 0)
  this.d.toBuffer(32).copy(buffer, 1)

  if (this.compressed) {
    buffer.writeUInt8(0x01, 33)
  }

  return bs58check.encode(buffer)
}

ECPair.prototype.getAddress = function () {
  var pubKey = this.getPublicKeyBuffer()
  var pubKeyHash = bcrypto.hash160(pubKey)

  var payload = new Buffer(21)
  payload.writeUInt8(this.network.pubKeyHash, 0)
  pubKeyHash.copy(payload, 1)

  return bs58check.encode(payload)
}

ECPair.prototype.getPublicKeyBuffer = function () {
  return this.Q.getEncoded(this.compressed)
}

ECPair.prototype.sign = function (hash) {
  if (!this.d) throw new Error('Missing private key')

  return ecdsa.sign(secp256k1, hash, this.d)
}

ECPair.prototype.verify = function (hash, signature) {
  return ecdsa.verify(secp256k1, hash, signature, this.Q)
}

module.exports = ECPair
<commit_msg>ECPair: makeRandom, fix comparison<commit_after>var bcrypto = require('./crypto')
var bs58check = require('bs58check')
var ecdsa = require('./ecdsa')
var ecurve = require('ecurve')
var randomBytes = require('randombytes')
var typeforce = require('typeforce')
var types = require('./types')

var NETWORKS = require('./networks')
var BigInteger = require('bigi')

var secp256k1 = ecurve.getCurveByName('secp256k1')

function ECPair (d, Q, options) {
  if (options) {
    typeforce({
      compressed: types.maybe(types.Boolean),
      network: types.maybe(types.Network)
    }, options)
  }

  options = options || {}

  if (d) {
    if (d.signum() <= 0) throw new Error('Private key must be greater than 0')
    if (d.compareTo(secp256k1.n) >= 0) throw new Error('Private key must be less than the curve order')
    if (Q) throw new TypeError('Unexpected publicKey parameter')

    this.d = d

  } else {
    typeforce(types.ECPoint, Q)

    this.__Q = Q
  }

  this.compressed = options.compressed === undefined ? true : options.compressed
  this.network = options.network || NETWORKS.bitcoin
}

Object.defineProperty(ECPair.prototype, 'Q', {
  get: function () {
    if (!this.__Q && this.d) {
      this.__Q = secp256k1.G.multiply(this.d)
    }

    return this.__Q
  }
})

ECPair.fromPublicKeyBuffer = function (buffer, network) {
  var Q = ecurve.Point.decodeFrom(secp256k1, buffer)

  return new ECPair(null, Q, {
    compressed: Q.compressed,
    network: network
  })
}

ECPair.fromWIF = function (string, networks) {
  var payload = bs58check.decode(string)
  var version = payload.readUInt8(0)
  var compressed

  if (payload.length === 34) {
    if (payload[33] !== 0x01) throw new Error('Invalid compression flag')

    // truncate the version/compression bytes
    payload = payload.slice(1, -1)
    compressed = true

  // no compression flag
  } else {
    if (payload.length !== 33) throw new Error('Invalid WIF payload length')

    // Truncate the version byte
    payload = payload.slice(1)
    compressed = false
  }

  var network

  // list of networks?
  if (Array.isArray(networks)) {
    network = networks.filter(function (network) {
      return version === network.wif
    }).pop() || {}

  // otherwise, assume a network object (or default to bitcoin)
  } else {
    network = networks || NETWORKS.bitcoin
  }

  if (version !== network.wif) throw new Error('Invalid network')

  var d = BigInteger.fromBuffer(payload)

  return new ECPair(d, null, {
    compressed: compressed,
    network: network
  })
}

ECPair.makeRandom = function (options) {
  options = options || {}

  var rng = options.rng || randomBytes

  var d
  do {
    var buffer = rng(32)
    typeforce(types.Buffer256bit, buffer)

    d = BigInteger.fromBuffer(buffer)
  } while (d.compareTo(secp256k1.n) >= 0)

  return new ECPair(d, null, options)
}

ECPair.prototype.toWIF = function () {
  if (!this.d) throw new Error('Missing private key')

  var bufferLen = this.compressed ? 34 : 33
  var buffer = new Buffer(bufferLen)

  buffer.writeUInt8(this.network.wif, 0)
  this.d.toBuffer(32).copy(buffer, 1)

  if (this.compressed) {
    buffer.writeUInt8(0x01, 33)
  }

  return bs58check.encode(buffer)
}

ECPair.prototype.getAddress = function () {
  var pubKey = this.getPublicKeyBuffer()
  var pubKeyHash = bcrypto.hash160(pubKey)

  var payload = new Buffer(21)
  payload.writeUInt8(this.network.pubKeyHash, 0)
  pubKeyHash.copy(payload, 1)

  return bs58check.encode(payload)
}

ECPair.prototype.getPublicKeyBuffer = function () {
  return this.Q.getEncoded(this.compressed)
}

ECPair.prototype.sign = function (hash) {
  if (!this.d) throw new Error('Missing private key')

  return ecdsa.sign(secp256k1, hash, this.d)
}

ECPair.prototype.verify = function (hash, signature) {
  return ecdsa.verify(secp256k1, hash, signature, this.Q)
}

module.exports = ECPair
<|endoftext|>