<commit_before>var fs = require('node-fs'); // Recursive directory creation. https://github.com/bpedro/node-fs

var _ = require('lodash'); // Utilities. http://underscorejs.org/
var Backbone = require('backbone'); // Data model utilities. http://backbonejs.org/
var moment = require('moment'); // Date processing. http://momentjs.com/
var child_process = require('child_process'); // http://nodejs.org/api/child_process.html
var XRegExp = require('xregexp').XRegExp; // Fancy regular expressions. http://xregexp.com/

var BaseModel = require('./baseModel.js').BaseModel;

// Manager of state which is displayed on the console, and responder of commands sent from the
// console.
exports.ConsoleState = BaseModel.extend({
    defaults: {
        isRunning: false,
        uptime: 0,
        fps: null,
        cpu: null,
        memory: null,
        canUpdate: false,
        isUpdating: false,
        configs: []
    },

    // The interval to update stats.
    _updateStatsTimeout: 0,
    _updateMemoryTimeout: 0,
    _updateStatsRate: 1000,

    // How many updates of historical data to keep.
    _statHistory: 60,
    _statIndex: 0,

    // A console process in which TYPEPERF is run continuously to update CPU usage.
    _cpuConsole: null,

    // A timeout to throttle the speed of updates to the console.
    _updateConsoleTimeout: 0,
    _updateConsoleRate: 1000 / 1,

    // The last time the app was running to compute uptime.
    _startupTime: 0,

    // Set up update loops.
    initialize: function() {
        BaseModel.prototype.initialize.apply(this);

        this.set('canUpdate', (($$contentUpdater.get('remote') && true) || ($$appUpdater.get('remote') && true)) === true);
        $$persistence.on('heart', this._onHeart, this);
        this._updateStats();
        this._updateCpu();
        this._updateMemory();
        $$network.transports.socketToConsole.sockets.on('connection', _.bind(this._onConnection, this));
    },

    // Build an object representing the whole configuration of the server. Sent to the console on
    // initial connection, also useful to spew for documentation.
    fullConfig: function(user) {
        var permissions = $$config.permissions ? $$config.permissions[user] : null;
        return {
            network: $$network.attributes,
            contentUpdater: $$contentUpdater.attributes,
            appUpdater: $$appUpdater.attributes,
            persistence: $$persistence.attributes,
            logging: $$logging.attributes,
            permissions: permissions
        };
    },

    // On initial socket connection with the console, listen for commands and send out the config.
    _onConnection: function(socket) {

        var username = socket.handshake.headers.authorization.match(/username="([^"]+)"/)[1];
        var permissions = $$config.permissions ? $$config.permissions[username] : null;

        // Responds to requests for appState updates, but throttle it to _updateConsoleRate.
        var updateConsole = _.bind(this._updateConsole, this);
        socket.on('appStateRequest', _.bind(function() {
            clearTimeout(this._updateConsoleTimeout);
            this._updateConsoleTimeout = setTimeout(updateConsole, this._updateConsoleRate);
        }, this));

        socket.on('start', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Startup requested from console.');
            $$serverState.saveState('runApp', true);
            $$persistence.startApp(true);
        }, this));

        socket.on('restart-app', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Restart requested from console.');
            $$serverState.saveState('runApp', true);
            $$persistence.restartApp(true);
        }, this));

        socket.on('shutdown-app', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Shutdown requested from console.');
            $$serverState.saveState('runApp', false);
            $$persistence.shutdownApp();
        }, this));

        socket.on('restart-pc', _.bind(function() {
            if (permissions && !permissions.computer) {
                return;
            }

            logger.info('Reboot requested from console.');
            $$persistence.restartMachine();
        }, this));

        socket.on('shutdown-pc', _.bind(function() {
            if (permissions && !permissions.computer) {
                return;
            }

            logger.info('Shutdown requested from console.');
            $$persistence.shutdownMachine();
        }, this));

        socket.on('setUpdaterSource', _.bind(function(updater, source) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Set updater source requested from console.');
            this.setUpdaterSource(updater, source);
        }, this));

        socket.on('updateUpdater', _.bind(function(updater) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Set update requested from console.');
            this.updateUpdater(updater);
        }, this));

        socket.on('rollbackUpdater', _.bind(function(updater) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Rollback requested from console.');
            this.rollbackUpdater(updater);
        }, this));

        socket.on('switchConfig', _.bind(function(config) {
            $$serverState.saveState('config', config, $$persistence.restartServer);
        }, this));

        $$network.transports.socketToConsole.sockets.emit('config', this.fullConfig(username), this.get('configs'));
    },

    // Send the console new data on an interval.
    _updateConsole: function() {
        var message = _.clone(this.attributes);
        message.restartCount = $$persistence.get('restartCount');
        message.logs = $$logging.get('logCache');
        message.events = $$logging.get('eventCache');
        message.canUpdate = this.get('canUpdate');

        message.updaters = {
            content: {
                isUpdating: $$contentUpdater.get('isUpdating'),
                canRollback: $$contentUpdater.get('canRollback'),
                source: $$contentUpdater.get('source')
            },
            app: {
                isUpdating: $$appUpdater.get('isUpdating'),
                canRollback: $$appUpdater.get('canRollback'),
                source: $$appUpdater.get('source')
            }
        };

        $$network.transports.socketToConsole.sockets.emit('appState', message);
    },

    // Update the internal objects which specify the FPS, whether the app is running, memory usage,
    // and uptime.
    _updateStats: function() {
        var fpsHistory = this.get('fps');

        if (!fpsHistory) {
            fpsHistory = [];
            this.set({
                fps: fpsHistory,
            });
        }

        // Update FPS.
        var fps = 1000 / (this._tickSum / this._maxTicks);
        fps *= 100;
        fps = Math.round(fps);
        fps /= 100;
        fpsHistory.push(fps);
        while (fpsHistory.length > this._statHistory) {
            fpsHistory.shift();
        }

        if ($$persistence.processId()) {
            // Update the uptime.
            var wasRunning = this.get('isRunning');
            if (!wasRunning) {
                this._startupTime = Date.now();
            }
            this.set('uptime', Date.now() - this._startupTime);
            this.set('isRunning', true);
        } else {
            // Not running, so reset everything.
            this.set({
                isRunning: false,
                fps: null,
                memory: null,
                uptime: 0
            });
        }

        clearTimeout(this._updateStatsTimeout);
        this._updateStatsTimeout = setTimeout(_.bind(this._updateStats, this), this._updateStatsRate);
    },

    // Request to update the memory.
    _updateMemory: function() {
        var id = $$persistence.processId();
        if (id) {
            child_process.exec('tasklist /FI "PID eq ' + id + '" /FO LIST', _.bind(function(error, stdout, stderror) {
                /*
                // tasklist.exe output looks like this:
                Image Name:   Client.exe
                PID:          12008
                Session Name: Console
                Session#:     1
                Mem Usage:    39,384 K
                */

                stdout = stdout.toString();
                var match = XRegExp.exec(stdout, XRegExp('[\\d,]+\\sK'));
                if (!match) {
                    return;
                }

                match = match[0]; // "39,384 K"
                match = match.replace(',', '').replace(' K', ''); // "39384"
                var memory = parseInt(match) * 1024; // 40329216

                var memoryHistory = this.get('memory');

                if (!memoryHistory) {
                    memoryHistory = [];
                    this.set({
                        memory: memoryHistory
                    });
                }

                memoryHistory.push(memory);
                while (memoryHistory.length > this._statHistory) {
                    memoryHistory.shift();
                }

                clearTimeout(this._updateMemoryTimeout);
                this._updateMemoryTimeout = setTimeout(_.bind(this._updateMemory, this), this._updateStatsRate);

                $$persistence.checkMemory(memory);
            }, this));
        } else {
            clearTimeout(this._updateMemoryTimeout);
            this._updateMemoryTimeout = setTimeout(_.bind(this._updateMemory, this), this._updateStatsRate);
        }
    },

    // Run typeperf to get total CPU usage -- haven't figured out how to get it per process.
    _updateCpu: function() {
        this._cpuConsole = child_process.spawn('typeperf', ['\\Processor(_Total)\\% Processor Time']);
        this._cpuConsole.stdout.on('data', _.bind(function(stdout) {

            stdout = stdout.toString();
            if (stdout && stdout.indexOf(',') === 0) {
                var cpu = parseFloat(stdout.substr(2, stdout.length - 3));
                if (!isNaN(cpu)) {
                    var cpuHistory = this.get('cpu');
                    if (!cpuHistory) {
                        cpuHistory = [];
                        this.set('cpu', cpuHistory);
                    }

                    cpuHistory.push(cpu);
                    while (cpuHistory.length > this._statHistory) {
                        cpuHistory.shift();
                    }
                }
            }
        }, this));
    },

    // Compute FPS in a fast way. http://stackoverflow.com/a/87732/468472
    _maxTicks: 180,
    _tickList: null,
    _tickSum: 0,
    _tickIndex: 0,
    _lastHeart: 0,

    // Update the FPS whenever a heartbeat message is received from the app.
    _onHeart: function(message) {
        if (!this._tickList) {
            this._tickList = [];
            while (this._tickList.length < this._maxTicks) {
                this._tickList.push(0);
            }
        }

        if (!this._lastHeart) {
            this._lastHeart = Date.now();
            this._lastFpsUpdate = this._lastHeart;
            return;
        }

        var newHeart = Date.now();
        var newTick = newHeart - this._lastHeart;
        this._lastHeart = newHeart;

        this._tickSum -= this._tickList[this._tickIndex];
        this._tickSum += newTick;
        this._tickList[this._tickIndex] = newTick;
        if (++this._tickIndex == this._maxTicks) {
            this._tickIndex = 0;
        }
    },

    // Change the source used by one of the updaters.
    setUpdaterSource: function(updater, source) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        if (source == updater.get('source')) {
            return;
        }

        updater.set('source', source);

        fs.exists(updater.get('temp')[source], _.bind(function(exists) {
            if (exists) {
                this._deployUpdater(updater, true);
            } else {
                this.updateUpdater(updater);
            }
        }, this));
    },

    // Trigger an update on one of the updaters.
    updateUpdater: function(updater, callback) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        if (!updater) {
            return;
        }

        logger.info('Updating ' + updater.get('name') + ' from ' + updater.get('source'));

        updater.download(_.bind(function(error) {
            if (error) {
                return;
            }

            logger.info(updater.get('name') + ' download complete! ' + (updater.get('needsUpdate') ? '' : 'Nothing new was found.'));
            if (!updater.get('needsUpdate')) {
                if (callback) {
                    callback();
                }
                return;
            }

            this._deployUpdater(updater, false);
        }, this));
    },

    // Deploy content downloaded by one of the updaters.
    _deployUpdater: function(updater, force) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        $$persistence.shutdownApp(_.bind(function() {
            updater.deploy(force, _.bind(function(error) {
                if (error) {
                    return;
                }

                logger.info(updater.get('name') + ' deploy complete!');
                if (updater.get('name') == 'app') {
                    $$persistence.restartServer();
                } else {
                    $$persistence.restartApp();
                }
            }, this));
        }, this));
    },

    // Shut down the app, roll back content, and restart it.
    rollbackUpdater: function(updater) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        $$persistence.shutdownApp(_.bind(function() {
            updater.rollBack(_.bind(function(error) {
                if (error) {
                    return;
                }

                logger.info('Rollback complete!');
                if (updater.get('name') == 'app') {
                    $$persistence.restartServer();
                } else {
                    $$persistence.restartApp();
                }
            }, this));
        }, this));
    }
});<commit_msg>don't break if there are no permissions<commit_after>var fs = require('node-fs'); // Recursive directory creation. https://github.com/bpedro/node-fs

var _ = require('lodash'); // Utilities. http://underscorejs.org/
var Backbone = require('backbone'); // Data model utilities. http://backbonejs.org/
var moment = require('moment'); // Date processing. http://momentjs.com/
var child_process = require('child_process'); // http://nodejs.org/api/child_process.html
var XRegExp = require('xregexp').XRegExp; // Fancy regular expressions. http://xregexp.com/

var BaseModel = require('./baseModel.js').BaseModel;

// Manager of state which is displayed on the console, and responder of commands sent from the
// console.
exports.ConsoleState = BaseModel.extend({
    defaults: {
        isRunning: false,
        uptime: 0,
        fps: null,
        cpu: null,
        memory: null,
        canUpdate: false,
        isUpdating: false,
        configs: []
    },

    // The interval to update stats.
    _updateStatsTimeout: 0,
    _updateMemoryTimeout: 0,
    _updateStatsRate: 1000,

    // How many updates of historical data to keep.
    _statHistory: 60,
    _statIndex: 0,

    // A console process in which TYPEPERF is run continuously to update CPU usage.
    _cpuConsole: null,

    // A timeout to throttle the speed of updates to the console.
    _updateConsoleTimeout: 0,
    _updateConsoleRate: 1000 / 1,

    // The last time the app was running to compute uptime.
    _startupTime: 0,

    // Set up update loops.
    initialize: function() {
        BaseModel.prototype.initialize.apply(this);

        this.set('canUpdate', (($$contentUpdater.get('remote') && true) || ($$appUpdater.get('remote') && true)) === true);
        $$persistence.on('heart', this._onHeart, this);
        this._updateStats();
        this._updateCpu();
        this._updateMemory();
        $$network.transports.socketToConsole.sockets.on('connection', _.bind(this._onConnection, this));
    },

    // Build an object representing the whole configuration of the server. Sent to the console on
    // initial connection, also useful to spew for documentation.
    fullConfig: function(user) {
        var permissions = $$config.permissions ? $$config.permissions[user] : null;
        return {
            network: $$network.attributes,
            contentUpdater: $$contentUpdater.attributes,
            appUpdater: $$appUpdater.attributes,
            persistence: $$persistence.attributes,
            logging: $$logging.attributes,
            permissions: permissions
        };
    },

    // On initial socket connection with the console, listen for commands and send out the config.
    _onConnection: function(socket) {

        var username = null;
        var permissions = null;
        if (socket.handshake.headers.authorization) {
            username = socket.handshake.headers.authorization.match(/username="([^"]+)"/)[1];
            permissions = $$config.permissions ? $$config.permissions[username] : null;
        }

        // Responds to requests for appState updates, but throttle it to _updateConsoleRate.
        var updateConsole = _.bind(this._updateConsole, this);
        socket.on('appStateRequest', _.bind(function() {
            clearTimeout(this._updateConsoleTimeout);
            this._updateConsoleTimeout = setTimeout(updateConsole, this._updateConsoleRate);
        }, this));

        socket.on('start', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Startup requested from console.');
            $$serverState.saveState('runApp', true);
            $$persistence.startApp(true);
        }, this));

        socket.on('restart-app', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Restart requested from console.');
            $$serverState.saveState('runApp', true);
            $$persistence.restartApp(true);
        }, this));

        socket.on('shutdown-app', _.bind(function() {
            if (permissions && !permissions.app) {
                return;
            }

            logger.info('Shutdown requested from console.');
            $$serverState.saveState('runApp', false);
            $$persistence.shutdownApp();
        }, this));

        socket.on('restart-pc', _.bind(function() {
            if (permissions && !permissions.computer) {
                return;
            }

            logger.info('Reboot requested from console.');
            $$persistence.restartMachine();
        }, this));

        socket.on('shutdown-pc', _.bind(function() {
            if (permissions && !permissions.computer) {
                return;
            }

            logger.info('Shutdown requested from console.');
            $$persistence.shutdownMachine();
        }, this));

        socket.on('setUpdaterSource', _.bind(function(updater, source) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Set updater source requested from console.');
            this.setUpdaterSource(updater, source);
        }, this));

        socket.on('updateUpdater', _.bind(function(updater) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Set update requested from console.');
            this.updateUpdater(updater);
        }, this));

        socket.on('rollbackUpdater', _.bind(function(updater) {
            if (permissions && !permissions.updaters) {
                return;
            }

            logger.info('Rollback requested from console.');
            this.rollbackUpdater(updater);
        }, this));

        socket.on('switchConfig', _.bind(function(config) {
            $$serverState.saveState('config', config, $$persistence.restartServer);
        }, this));

        $$network.transports.socketToConsole.sockets.emit('config', this.fullConfig(username), this.get('configs'));
    },

    // Send the console new data on an interval.
    _updateConsole: function() {
        var message = _.clone(this.attributes);
        message.restartCount = $$persistence.get('restartCount');
        message.logs = $$logging.get('logCache');
        message.events = $$logging.get('eventCache');
        message.canUpdate = this.get('canUpdate');

        message.updaters = {
            content: {
                isUpdating: $$contentUpdater.get('isUpdating'),
                canRollback: $$contentUpdater.get('canRollback'),
                source: $$contentUpdater.get('source')
            },
            app: {
                isUpdating: $$appUpdater.get('isUpdating'),
                canRollback: $$appUpdater.get('canRollback'),
                source: $$appUpdater.get('source')
            }
        };

        $$network.transports.socketToConsole.sockets.emit('appState', message);
    },

    // Update the internal objects which specify the FPS, whether the app is running, memory usage,
    // and uptime.
    _updateStats: function() {
        var fpsHistory = this.get('fps');

        if (!fpsHistory) {
            fpsHistory = [];
            this.set({
                fps: fpsHistory,
            });
        }

        // Update FPS.
        var fps = 1000 / (this._tickSum / this._maxTicks);
        fps *= 100;
        fps = Math.round(fps);
        fps /= 100;
        fpsHistory.push(fps);
        while (fpsHistory.length > this._statHistory) {
            fpsHistory.shift();
        }

        if ($$persistence.processId()) {
            // Update the uptime.
            var wasRunning = this.get('isRunning');
            if (!wasRunning) {
                this._startupTime = Date.now();
            }
            this.set('uptime', Date.now() - this._startupTime);
            this.set('isRunning', true);
        } else {
            // Not running, so reset everything.
            this.set({
                isRunning: false,
                fps: null,
                memory: null,
                uptime: 0
            });
        }

        clearTimeout(this._updateStatsTimeout);
        this._updateStatsTimeout = setTimeout(_.bind(this._updateStats, this), this._updateStatsRate);
    },

    // Request to update the memory.
    _updateMemory: function() {
        var id = $$persistence.processId();
        if (id) {
            child_process.exec('tasklist /FI "PID eq ' + id + '" /FO LIST', _.bind(function(error, stdout, stderror) {
                /*
                // tasklist.exe output looks like this:
                Image Name:   Client.exe
                PID:          12008
                Session Name: Console
                Session#:     1
                Mem Usage:    39,384 K
                */

                stdout = stdout.toString();
                var match = XRegExp.exec(stdout, XRegExp('[\\d,]+\\sK'));
                if (!match) {
                    return;
                }

                match = match[0]; // "39,384 K"
                match = match.replace(',', '').replace(' K', ''); // "39384"
                var memory = parseInt(match) * 1024; // 40329216

                var memoryHistory = this.get('memory');

                if (!memoryHistory) {
                    memoryHistory = [];
                    this.set({
                        memory: memoryHistory
                    });
                }

                memoryHistory.push(memory);
                while (memoryHistory.length > this._statHistory) {
                    memoryHistory.shift();
                }

                clearTimeout(this._updateMemoryTimeout);
                this._updateMemoryTimeout = setTimeout(_.bind(this._updateMemory, this), this._updateStatsRate);

                $$persistence.checkMemory(memory);
            }, this));
        } else {
            clearTimeout(this._updateMemoryTimeout);
            this._updateMemoryTimeout = setTimeout(_.bind(this._updateMemory, this), this._updateStatsRate);
        }
    },

    // Run typeperf to get total CPU usage -- haven't figured out how to get it per process.
    _updateCpu: function() {
        this._cpuConsole = child_process.spawn('typeperf', ['\\Processor(_Total)\\% Processor Time']);
        this._cpuConsole.stdout.on('data', _.bind(function(stdout) {

            stdout = stdout.toString();
            if (stdout && stdout.indexOf(',') === 0) {
                var cpu = parseFloat(stdout.substr(2, stdout.length - 3));
                if (!isNaN(cpu)) {
                    var cpuHistory = this.get('cpu');
                    if (!cpuHistory) {
                        cpuHistory = [];
                        this.set('cpu', cpuHistory);
                    }

                    cpuHistory.push(cpu);
                    while (cpuHistory.length > this._statHistory) {
                        cpuHistory.shift();
                    }
                }
            }
        }, this));
    },

    // Compute FPS in a fast way. http://stackoverflow.com/a/87732/468472
    _maxTicks: 180,
    _tickList: null,
    _tickSum: 0,
    _tickIndex: 0,
    _lastHeart: 0,

    // Update the FPS whenever a heartbeat message is received from the app.
    _onHeart: function(message) {
        if (!this._tickList) {
            this._tickList = [];
            while (this._tickList.length < this._maxTicks) {
                this._tickList.push(0);
            }
        }

        if (!this._lastHeart) {
            this._lastHeart = Date.now();
            this._lastFpsUpdate = this._lastHeart;
            return;
        }

        var newHeart = Date.now();
        var newTick = newHeart - this._lastHeart;
        this._lastHeart = newHeart;

        this._tickSum -= this._tickList[this._tickIndex];
        this._tickSum += newTick;
        this._tickList[this._tickIndex] = newTick;
        if (++this._tickIndex == this._maxTicks) {
            this._tickIndex = 0;
        }
    },

    // Change the source used by one of the updaters.
    setUpdaterSource: function(updater, source) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        if (source == updater.get('source')) {
            return;
        }

        updater.set('source', source);

        fs.exists(updater.get('temp')[source], _.bind(function(exists) {
            if (exists) {
                this._deployUpdater(updater, true);
            } else {
                this.updateUpdater(updater);
            }
        }, this));
    },

    // Trigger an update on one of the updaters.
    updateUpdater: function(updater, callback) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        if (!updater) {
            return;
        }

        logger.info('Updating ' + updater.get('name') + ' from ' + updater.get('source'));

        updater.download(_.bind(function(error) {
            if (error) {
                return;
            }

            logger.info(updater.get('name') + ' download complete! ' + (updater.get('needsUpdate') ? '' : 'Nothing new was found.'));
            if (!updater.get('needsUpdate')) {
                if (callback) {
                    callback();
                }
                return;
            }

            this._deployUpdater(updater, false);
        }, this));
    },

    // Deploy content downloaded by one of the updaters.
    _deployUpdater: function(updater, force) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        $$persistence.shutdownApp(_.bind(function() {
            updater.deploy(force, _.bind(function(error) {
                if (error) {
                    return;
                }

                logger.info(updater.get('name') + ' deploy complete!');
                if (updater.get('name') == 'app') {
                    $$persistence.restartServer();
                } else {
                    $$persistence.restartApp();
                }
            }, this));
        }, this));
    },

    // Shut down the app, roll back content, and restart it.
    rollbackUpdater: function(updater) {
        if (_.isString(updater)) {
            updater = global['$$' + updater + 'Updater'];
        }

        $$persistence.shutdownApp(_.bind(function() {
            updater.rollBack(_.bind(function(error) {
                if (error) {
                    return;
                }

                logger.info('Rollback complete!');
                if (updater.get('name') == 'app') {
                    $$persistence.restartServer();
                } else {
                    $$persistence.restartApp();
                }
            }, this));
        }, this));
    }
});<|endoftext|>


<commit_before>(function () {
'use strict';
var website = openerp.website;

website.if_dom_contains('div.o_website_quote', function () {

    $('a.js_update_line_json').on('click', function (ev) {
        ev.preventDefault();
        var $link = $(ev.currentTarget);
        var href = $link.attr("href");
        var order_id = href.match(/order_id=([0-9]+)/);
        var line_id = href.match(/update_line\/([0-9]+)/);
        var token = href.match(/token=(.*)/);
        openerp.jsonRpc("/quote/update_line", 'call', {
                'line_id': line_id[1],
                'order_id': parseInt(order_id[1]),
                'token': token[1],
                'remove': $link.is('[href*="remove"]'),
                'unlink': $link.is('[href*="unlink"]')
                })
                .then(function (data) {
                    if(!data){
                        location.reload();
                    }
                    $link.parents('.input-group:first').find('.js_quantity').val(data[0]);
                    $('[data-id="total_amount"]>span').html(data[1]);
                });
        return false;
    });

    var empty_sign = false;
    $('#modelaccept').on('shown.bs.modal', function (e) {
        $("#signature").empty().jSignature({'decor-color' : '#D1D0CE'});
        empty_sign = $("#signature").jSignature("getData",'image');
    });

    $('#sign_clean').on('click', function (e) {
        $("#signature").jSignature('reset');
    });

    $('form.js_accept_json').submit(function(ev){
        ev.preventDefault();
        var $link = $(ev.currentTarget);
        var $confirm_btn = $link.find('button[type="submit"]');
        var href = $link.attr("action");
        var order_id = href.match(/accept\/([0-9]+)/);
        var token = href.match(/token=(.*)/);
        if (token)
            token = token[1];
        var signer_name = $("#name").val();
        var sign = $("#signature").jSignature("getData",'image');
        var is_empty = sign?empty_sign[1]==sign[1]:false;
        $('#signer').toggleClass('has-error', ! signer_name);
        $('#drawsign').toggleClass('panel-danger', is_empty).toggleClass('panel-default', ! is_empty);

        if (is_empty || ! signer_name)
            return false;

        $confirm_btn.prepend(' ');
        $confirm_btn.attr('disabled', true);
        openerp.jsonRpc("/quote/accept", 'call', {
            'order_id': parseInt(order_id[1]),
            'token': token,
            'signer': signer_name,
            'sign': sign?JSON.stringify(sign[1]):false,
        }).then(function (data) {
            var message_id = (data) ? 3 : 4;
            $('#modelaccept').modal('hide');
            window.location.href = '/quote/'+order_id[1]+'/'+token+'?message='+message_id;
        });
        return false;
    });

    // automatically generate a menu from h1 and h2 tag in content
    var $container = $('body[data-target=".navspy"]');
    var ul = $('[data-id="quote_sidebar"]', $container);
    var sub_li = null;
    var sub_ul = null;
    $("[id^=quote_header_], [id^=quote_]", $container).attr("id", "");
    $("h1, h2", $container).each(function() {
        var id;
        switch (this.tagName.toLowerCase()) {
            case "h1":
                id = _.uniqueId('quote_header_');
                $(this.parentNode).attr('id',id);
                sub_li = $("
").html(''+$(this).text()+'').appendTo(ul);
                sub_ul = null;
                break;
            case "h2":
                id = _.uniqueId('quote_');
                if (sub_li) {
                    if (!sub_ul) {
                        sub_ul = $("
").appendTo(sub_li);
                    }
                    $(this.parentNode).attr('id',id);
                    $("
").html(''+$(this).text()+'').appendTo(sub_ul);
                }
                break;
            }
    });
});

}());
<commit_msg>[FIX] website_quote: don't unescape menu title<commit_after>(function () {
'use strict';
var website = openerp.website;

website.if_dom_contains('div.o_website_quote', function () {

    $('a.js_update_line_json').on('click', function (ev) {
        ev.preventDefault();
        var $link = $(ev.currentTarget);
        var href = $link.attr("href");
        var order_id = href.match(/order_id=([0-9]+)/);
        var line_id = href.match(/update_line\/([0-9]+)/);
        var token = href.match(/token=(.*)/);
        openerp.jsonRpc("/quote/update_line", 'call', {
                'line_id': line_id[1],
                'order_id': parseInt(order_id[1]),
                'token': token[1],
                'remove': $link.is('[href*="remove"]'),
                'unlink': $link.is('[href*="unlink"]')
                })
                .then(function (data) {
                    if(!data){
                        location.reload();
                    }
                    $link.parents('.input-group:first').find('.js_quantity').val(data[0]);
                    $('[data-id="total_amount"]>span').html(data[1]);
                });
        return false;
    });

    var empty_sign = false;
    $('#modelaccept').on('shown.bs.modal', function (e) {
        $("#signature").empty().jSignature({'decor-color' : '#D1D0CE'});
        empty_sign = $("#signature").jSignature("getData",'image');
    });

    $('#sign_clean').on('click', function (e) {
        $("#signature").jSignature('reset');
    });

    $('form.js_accept_json').submit(function(ev){
        ev.preventDefault();
        var $link = $(ev.currentTarget);
        var $confirm_btn = $link.find('button[type="submit"]');
        var href = $link.attr("action");
        var order_id = href.match(/accept\/([0-9]+)/);
        var token = href.match(/token=(.*)/);
        if (token)
            token = token[1];
        var signer_name = $("#name").val();
        var sign = $("#signature").jSignature("getData",'image');
        var is_empty = sign?empty_sign[1]==sign[1]:false;
        $('#signer').toggleClass('has-error', ! signer_name);
        $('#drawsign').toggleClass('panel-danger', is_empty).toggleClass('panel-default', ! is_empty);

        if (is_empty || ! signer_name)
            return false;

        $confirm_btn.prepend(' ');
        $confirm_btn.attr('disabled', true);
        openerp.jsonRpc("/quote/accept", 'call', {
            'order_id': parseInt(order_id[1]),
            'token': token,
            'signer': signer_name,
            'sign': sign?JSON.stringify(sign[1]):false,
        }).then(function (data) {
            var message_id = (data) ? 3 : 4;
            $('#modelaccept').modal('hide');
            window.location.href = '/quote/'+order_id[1]+'/'+token+'?message='+message_id;
        });
        return false;
    });

    // automatically generate a menu from h1 and h2 tag in content
    var $container = $('body[data-target=".navspy"]');
    var ul = $('[data-id="quote_sidebar"]', $container);
    var sub_li = null;
    var sub_ul = null;
    $("[id^=quote_header_], [id^=quote_]", $container).attr("id", "");
    $("h1, h2", $container).each(function() {
        var id;
        switch (this.tagName.toLowerCase()) {
            case "h1":
                id = _.uniqueId('quote_header_');
                $(this.parentNode).attr('id',id);
                sub_li = $("
").html('').text($(this).text()).appendTo(ul);
                sub_ul = null;
                break;
            case "h2":
                id = _.uniqueId('quote_');
                if (sub_li) {
                    if (!sub_ul) {
                        sub_ul = $("
").appendTo(sub_li);
                    }
                    $(this.parentNode).attr('id',id);
                    $("
").html('').text($(this).text()).appendTo(sub_ul);
                }
                break;
            }
    });
});

}());
<|endoftext|>


<commit_before>function ThreeObject3DPlugin(core) {
	Plugin.apply(this, arguments)

	this.desc = 'THREE.js Object3D'

	this.input_slots = [
		{ name: 'position', dt: core.datatypes.VECTOR },
		{ name: 'rotation', dt: core.datatypes.VECTOR },
		{ name: 'scale', dt: core.datatypes.VECTOR, def: new THREE.Vector3(1, 1, 1) },

		{ name: 'visible', dt: core.datatypes.BOOL, def: true },
		{ name: 'castShadow', dt: core.datatypes.BOOL, def: true, label: "Cast shadow" },
		{ name: 'receiveShadow', dt: core.datatypes.BOOL, def: true, label: "Receive shadows" },

		{ name: 'name', dt: core.datatypes.TEXT, def: ''},

		{
			name:   'stereo view',
			dt:     core.datatypes.FLOAT,
			def:    0,
			desc:   'Affects how this object is rendered in stereo
Stereo View - 0: both eyes, 1: left eye only, 2: right eye only, 3: mono view only'
		},
		{
			name:   'lock transform',
			dt:     core.datatypes.BOOL,
			def:    false,
			label:  "Lock transform controls",
			desc:   'if enabled, this object\'s transform is locked and can\'t be adjusted in the 3d editor.',
			patchable: false
		}
	]

	this.output_slots = [{
		name: 'object3d',
		dt: core.datatypes.OBJECT3D
	}]

	this.state = {
		position: {x: 0, y: 0, z:0},
		scale: {x: 1, y: 1, z:1},

		// names with underscores have to match with THREE.Quaternion
		// member variable names because of to/from json serialisation
		quaternion: {_x: 0, _y: 0, _z:0, _w:1}
	}

	this.pivot = {x: 0, y: 0, z: 0}

	this.lockTransformControls = false

	this.graphInputs = {
		position: new THREE.Vector3(0, 0, 0),
		scale: new THREE.Vector3(0, 0, 0),
		quaternion: new THREE.Quaternion(0, 0, 0, 1)
	}
}

ThreeObject3DPlugin.prototype = Object.create(Plugin.prototype)

ThreeObject3DPlugin.prototype.reset = function() {
	Plugin.prototype.reset.apply(this, arguments)

	if (!this.object3d)
		this.setObject3D(new THREE.Object3D())

	this.updateTransforms()
}

ThreeObject3DPlugin.prototype.setObject3D = function(newObject3d) {
	this.object3d = newObject3d

	var that = this
	this.object3d.traverse(function(n) {
		n.castShadow = that.inputValues.castShadow
		n.receiveShadow = that.inputValues.receiveShadow
	})

	function hierarchyChanged(event) {
		var obj = event.target
		var castShadow = obj.castShadow
		var receiveShadow = obj.receiveShadow

		obj.traverse(function (n) {
			n.castShadow = castShadow
			n.receiveShadow = receiveShadow
		})
	}

	this.object3d.addEventListener('added', hierarchyChanged)

	// back reference for object picking
	this.object3d.backReference = this
}

ThreeObject3DPlugin.prototype.update_input = function(slot, data) {
	if (!this.object3d)
		return;

	this.inputValues[slot.name] = data

	var that = this

	var handlers = {
		"position": function() {
			that.graphInputs.position.x = data.x
			that.graphInputs.position.y = data.y
			that.graphInputs.position.z = data.z
		},
		"rotation": function() {
			that.graphInputs.quaternion.setFromEuler(new THREE.Euler(data.x, data.y, data.z, "YZX"))
		},
		"scale": function() {
			that.graphInputs.scale.x = data.x
			that.graphInputs.scale.y = data.y
			that.graphInputs.scale.z = data.z
		},
		"visible": function() {
			that.object3d.visible = data
		},
		"castShadow": function() {
			that.object3d.traverse(function (n) {
				n.castShadow = data
			})
		},
		"receiveShadow": function() {
			that.object3d.traverse(function (n) {
				n.receiveShadow = data
			})
		},
		"name": function() {
			that.object3d.name = data
		},
		"stereo view": function() {
			that.object3d.traverse(function (n) {
				n.layers.set(data)
			})
		},
		"lock transform": function() {
			that.lockTransformControls = data
		}
	}

	if (handlers[slot.name]) {
		if (data !== undefined) {
			handlers[slot.name]()
		}
	}
	else {
		if (this.object3d[slot.name] instanceof THREE.Color) {
			this.object3d[slot.name].copy(data)
		}
		else {
			this.object3d[slot.name] = data
		}
	}
}

ThreeObject3DPlugin.prototype.update_output = function() {
	return this.object3d
}

ThreeObject3DPlugin.prototype.state_changed = function(ui) {
	if (ui) {
		return
	}
}

ThreeObject3DPlugin.prototype.updateTransforms = function() {
	this.object3d.scale.set(
		this.graphInputs.scale.x + this.state.scale.x,
		this.graphInputs.scale.y + this.state.scale.y,
		this.graphInputs.scale.z + this.state.scale.z)

	this.object3d.position.set(
		this.graphInputs.position.x + this.state.position.x + this.pivot.x,
		this.graphInputs.position.y + this.state.position.y + this.pivot.y,
		this.graphInputs.position.z + this.state.position.z + this.pivot.z)

	this.object3d.quaternion.set(
		this.state.quaternion._x,
		this.state.quaternion._y,
		this.state.quaternion._z,
		this.state.quaternion._w)

	this.object3d.quaternion.multiply(this.graphInputs.quaternion)
}

ThreeObject3DPlugin.prototype.update_state = function() {
	if (this.object3d.layers !== this.inputValues['stereo view']) {
		var that = this
		this.object3d.traverse(function(n) {
			n.layers.set(that.inputValues['stereo view'])
		})
	}

	this.updateTransforms()
}

ThreeObject3DPlugin.prototype.canEditPosition = function() {
	return !this.lockTransformControls
}

ThreeObject3DPlugin.prototype.canEditQuaternion = function() {
	return !this.lockTransformControls
}

ThreeObject3DPlugin.prototype.canEditScale = function() {
	return !this.lockTransformControls
}

if (typeof(module) !== 'undefined')
	module.exports = ThreeObject3DPlugin
<commit_msg>Revert "Revert "object3d scaling should multiply, not add to graph input""<commit_after>function ThreeObject3DPlugin(core) {
	Plugin.apply(this, arguments)

	this.desc = 'THREE.js Object3D'

	this.input_slots = [
		{ name: 'position', dt: core.datatypes.VECTOR },
		{ name: 'rotation', dt: core.datatypes.VECTOR },
		{ name: 'scale', dt: core.datatypes.VECTOR, def: new THREE.Vector3(1, 1, 1) },

		{ name: 'visible', dt: core.datatypes.BOOL, def: true },
		{ name: 'castShadow', dt: core.datatypes.BOOL, def: true, label: "Cast shadow" },
		{ name: 'receiveShadow', dt: core.datatypes.BOOL, def: true, label: "Receive shadows" },

		{ name: 'name', dt: core.datatypes.TEXT, def: ''},

		{
			name:   'stereo view',
			dt:     core.datatypes.FLOAT,
			def:    0,
			desc:   'Affects how this object is rendered in stereo
Stereo View - 0: both eyes, 1: left eye only, 2: right eye only, 3: mono view only'
		},
		{
			name:   'lock transform',
			dt:     core.datatypes.BOOL,
			def:    false,
			label:  "Lock transform controls",
			desc:   'if enabled, this object\'s transform is locked and can\'t be adjusted in the 3d editor.',
			patchable: false
		}
	]

	this.output_slots = [{
		name: 'object3d',
		dt: core.datatypes.OBJECT3D
	}]

	this.state = {
		position: {x: 0, y: 0, z:0},
		scale: {x: 1, y: 1, z:1},

		// names with underscores have to match with THREE.Quaternion
		// member variable names because of to/from json serialisation
		quaternion: {_x: 0, _y: 0, _z:0, _w:1}
	}

	this.pivot = {x: 0, y: 0, z: 0}

	this.lockTransformControls = false

	this.graphInputs = {
		position: new THREE.Vector3(0, 0, 0),
		scale: new THREE.Vector3(1, 1, 1),
		quaternion: new THREE.Quaternion(0, 0, 0, 1)
	}
}

ThreeObject3DPlugin.prototype = Object.create(Plugin.prototype)

ThreeObject3DPlugin.prototype.reset = function() {
	Plugin.prototype.reset.apply(this, arguments)

	if (!this.object3d)
		this.setObject3D(new THREE.Object3D())

	this.updateTransforms()
}

ThreeObject3DPlugin.prototype.setObject3D = function(newObject3d) {
	this.object3d = newObject3d

	var that = this
	this.object3d.traverse(function(n) {
		n.castShadow = that.inputValues.castShadow
		n.receiveShadow = that.inputValues.receiveShadow
	})

	function hierarchyChanged(event) {
		var obj = event.target
		var castShadow = obj.castShadow
		var receiveShadow = obj.receiveShadow

		obj.traverse(function (n) {
			n.castShadow = castShadow
			n.receiveShadow = receiveShadow
		})
	}

	this.object3d.addEventListener('added', hierarchyChanged)

	// back reference for object picking
	this.object3d.backReference = this
}

ThreeObject3DPlugin.prototype.update_input = function(slot, data) {
	if (!this.object3d)
		return;

	this.inputValues[slot.name] = data

	var that = this

	var handlers = {
		"position": function() {
			that.graphInputs.position.x = data.x
			that.graphInputs.position.y = data.y
			that.graphInputs.position.z = data.z
		},
		"rotation": function() {
			that.graphInputs.quaternion.setFromEuler(new THREE.Euler(data.x, data.y, data.z, "YZX"))
		},
		"scale": function() {
			that.graphInputs.scale.x = data.x
			that.graphInputs.scale.y = data.y
			that.graphInputs.scale.z = data.z
		},
		"visible": function() {
			that.object3d.visible = data
		},
		"castShadow": function() {
			that.object3d.traverse(function (n) {
				n.castShadow = data
			})
		},
		"receiveShadow": function() {
			that.object3d.traverse(function (n) {
				n.receiveShadow = data
			})
		},
		"name": function() {
			that.object3d.name = data
		},
		"stereo view": function() {
			that.object3d.traverse(function (n) {
				n.layers.set(data)
			})
		},
		"lock transform": function() {
			that.lockTransformControls = data
		}
	}

	if (handlers[slot.name]) {
		if (data !== undefined) {
			handlers[slot.name]()
		}
	}
	else {
		if (this.object3d[slot.name] instanceof THREE.Color) {
			this.object3d[slot.name].copy(data)
		}
		else {
			this.object3d[slot.name] = data
		}
	}
}

ThreeObject3DPlugin.prototype.update_output = function() {
	return this.object3d
}

ThreeObject3DPlugin.prototype.state_changed = function(ui) {
	if (ui) {
		return
	}
}

ThreeObject3DPlugin.prototype.updateTransforms = function() {
	this.object3d.scale.set(
		this.graphInputs.scale.x * this.state.scale.x,
		this.graphInputs.scale.y * this.state.scale.y,
		this.graphInputs.scale.z * this.state.scale.z)

	this.object3d.position.set(
		this.graphInputs.position.x + this.state.position.x + this.pivot.x,
		this.graphInputs.position.y + this.state.position.y + this.pivot.y,
		this.graphInputs.position.z + this.state.position.z + this.pivot.z)

	this.object3d.quaternion.set(
		this.state.quaternion._x,
		this.state.quaternion._y,
		this.state.quaternion._z,
		this.state.quaternion._w)

	this.object3d.quaternion.multiply(this.graphInputs.quaternion)
}

ThreeObject3DPlugin.prototype.update_state = function() {
	if (this.object3d.layers !== this.inputValues['stereo view']) {
		var that = this
		this.object3d.traverse(function(n) {
			n.layers.set(that.inputValues['stereo view'])
		})
	}

	this.updateTransforms()
}

ThreeObject3DPlugin.prototype.canEditPosition = function() {
	return !this.lockTransformControls
}

ThreeObject3DPlugin.prototype.canEditQuaternion = function() {
	return !this.lockTransformControls
}

ThreeObject3DPlugin.prototype.canEditScale = function() {
	return !this.lockTransformControls
}

if (typeof(module) !== 'undefined')
	module.exports = ThreeObject3DPlugin
<|endoftext|>


<commit_before>/**
  Handles routes related to users in the admin section.

  @class AdminUserRoute
  @extends Discourse.Route
  @namespace Discourse
  @module Discourse
**/
Discourse.AdminUserRoute = Discourse.Route.extend({
  serialize: function(params) {
    return { username: Em.get(params, 'username').toLowerCase() };
  },

  renderTemplate: function() {
    this.render('admin/templates/user', {into: 'admin/templates/admin'});
  },

  setupController: function(controller, model) {
    Discourse.AdminUser.find(Em.get(model, 'username').toLowerCase()).then(function (u) {
      controller.set('content', u);
    });
  },

});
<commit_msg>Fix: comma<commit_after>/**
  Handles routes related to users in the admin section.

  @class AdminUserRoute
  @extends Discourse.Route
  @namespace Discourse
  @module Discourse
**/
Discourse.AdminUserRoute = Discourse.Route.extend({
  serialize: function(params) {
    return { username: Em.get(params, 'username').toLowerCase() };
  },

  renderTemplate: function() {
    this.render('admin/templates/user', {into: 'admin/templates/admin'});
  },

  setupController: function(controller, model) {
    Discourse.AdminUser.find(Em.get(model, 'username').toLowerCase()).then(function (u) {
      controller.set('content', u);
    });
  }

});
<|endoftext|>


<commit_before>function shouldDisplay(element, schoolContext) {
  switch(element) {
  case 'house':
    if (schoolContext && schoolContext.local_id) {
      return "SHS" === schoolContext.local_id;
    }
    return false;
  case 'counselor':
    if (schoolContext && schoolContext.school_type) {
      return "HS" === schoolContext.school_type;
    }
    return false;
  default:
    return false;
  }
}

export { shouldDisplay }; <commit_msg>Changing name of var from schoolContext to plain old school.<commit_after>function shouldDisplay(element, school) {
  switch(element) {
  case 'house':
    if (school && school.local_id) {
      return "SHS" === school.local_id;
    }
    return false;
  case 'counselor':
    if (school && school.school_type) {
      return "HS" === school.school_type;
    }
    return false;
  default:
    return false;
  }
}

export { shouldDisplay }; <|endoftext|>


<commit_before>let ErrorDisplay = require('shared/show_errors');
let MobileCheck = require('member-facing/backbone/mobile_check');
const GlobalEvents = require('shared/global_events');

const Survey = Backbone.View.extend({

  el: '.survey',
  HIDDEN_FIELDS: ['source', 'referrer_id'],

  events: {
    'click .survey__skip-button': 'skipSection',
    'ajax:success form.survey__form': 'handleSuccess',
    'ajax:error form.survey__form': 'handleFailure',
    'ajax:send form.survey__form': 'handleSend',
  },

  // options: object with any of the following keys
  //    source: the referring source to save
  //    referrer_id: the champaign id of the referrer
  //    prefill: an object with fields that will prefill the form
  //    followUpUrl: the url to redirect to after the survey is completed
  initialize(options={}) {
    this.$forms = this.$('.survey__form');
    if (!MobileCheck.isMobile()) {
      this.selectizeCountry();
    }
    this.insertHiddenFields(options);
    this.prefill(options.prefill);
    this.revealFirstForm();
    this.submitFirstFormIfComplete();
    this.followUpUrl = options.followUpUrl;
    GlobalEvents.bindEvents(this);
  },

  revealFirstForm() {
    this.$('.survey__form').first().removeClass('hidden-closed');
  },

  selectizeCountry() {
    this.$('.action-form__country-selector').selectize();
  },

  // prefillValues - an object mapping form names to prefill values
  prefill(prefillValues) {
    this.prefillValues = prefillValues
    if(!_.isObject(prefillValues)) { return; }
    this.$('.survey__form input, select').each((ii, field) => {
      let $field = $(field);
      let name = $field.prop('name');
      if (prefillValues.hasOwnProperty(name)) {
        let radioOrCheck = ($field.prop('type') === 'radio' || $field.prop('type') === 'checkbox');
        if (radioOrCheck && $field.val() == prefillValues[name]) {
          $field.prop('checked', 'checked');
        } else {
          $field.val(prefillValues[name]);
        }
      }
    });
  },

  submitFirstFormIfComplete() {
    // form.serialize() returns the forms values as URL encoded params,
    // so `=&` only happens when there's a blank value in the form, eg
    // name=&weight=180, and `=` at the end means the last param is blank
    let $form = this.$forms.first();
    let serialized = $form.serialize();
    let noneEmpty = (serialized.indexOf('=&') === -1 && serialized.slice(-1) !== '=');
    let allPresent = _.every($form.find('input, select'), (el) => {
      if (this.$(el).prop('type') === 'hidden') return true;
      let name = this.$(el).prop('name');
      let outputIncludesName = (serialized.indexOf(`&${name}=`) !== -1 ||
                                serialized.indexOf(`${name}=`) === -0);
      let prefillIncludesName = (this.prefillValues[name] !== undefined);
      return outputIncludesName && prefillIncludesName;
    });
    if (noneEmpty && allPresent) $form.submit();
  },

  insertHiddenFields(options) {
    for(var ii = 0; ii < this.HIDDEN_FIELDS.length; ii++) {
      let field = this.HIDDEN_FIELDS[ii];
      if(options[field] && this.$el) {
        this.insertHiddenInput(field, options[field], this.$forms.first());
      }
    }
  },

  insertHiddenInput(name, value, element) {
    $('<input>').attr({
      type: 'hidden',
      name: name,
      value: value
    }).appendTo(element);
  },

  handleSuccess(e, data){
    ErrorDisplay.clearErrors(this.$(e.target));
    this.enableButton(e);
    this.followForm(this.$(e.target));
  },

  handleFailure(e, data) {
    ErrorDisplay.clearErrors(this.$(e.target));
    ErrorDisplay.show(e, data);
    this.enableButton(e);
  },

  handleSend(e) {
    ErrorDisplay.clearErrors(this.$(e.target));
    this.disableButton(e);
  },

  // we assume all sections are skippable, since required sections should
  // not have a visible skip button
  skipSection(e) {
    this.followForm(this.$(e.target).parents('.survey__form'));
  },

  followForm($form) {
    const $nextForm = $form.next('.survey__form');
    if ($nextForm.length) {
      this.revealForm($nextForm);
    } else {
      this.followUp();
    }
  },

  revealForm($form) {
    $form.removeClass('hidden-closed');
    $('html, body').animate({ scrollTop: $form.offset().top }, 500);
  },

  followUp() {
    if (this.followUpUrl) {
      this.redirectTo(this.followUpUrl);
    } else {
      window.alert('Thanks for completing our survey!');
    }
  },

  redirectTo(url) {
    window.location.href = url
  },

  disableButton(e) {
    this.$(e.target).find('.survey__button').each((ii, el) => {
      let $btn = this.$(el);
      if ($btn.data('enabled-text') === undefined) {
        $btn.data('enabled-text', $btn.text());
      }
      if (!$btn.hasClass('survey__skip-button')) {
        $btn.text(I18n.t('form.processing'))
      }
      $btn.addClass('button--disabled');
    });
  },

  enableButton(e) {
    this.$(e.target).find('.survey__button').each((ii, el) => {
      let $btn = this.$(el);
      $btn.text($btn.data('enabled-text'));
      $btn.removeClass('button--disabled');
    });
  },
});

module.exports = Survey;
<commit_msg>leave room for header when scrolling between survey sections<commit_after>let ErrorDisplay = require('shared/show_errors');
let MobileCheck = require('member-facing/backbone/mobile_check');
const GlobalEvents = require('shared/global_events');

const Survey = Backbone.View.extend({

  el: '.survey',
  HIDDEN_FIELDS: ['source', 'referrer_id'],
  DEFAULT_SCROLL_OFFSET: 80,

  events: {
    'click .survey__skip-button': 'skipSection',
    'ajax:success form.survey__form': 'handleSuccess',
    'ajax:error form.survey__form': 'handleFailure',
    'ajax:send form.survey__form': 'handleSend',
  },

  // options: object with any of the following keys
  //    source: the referring source to save
  //    referrer_id: the champaign id of the referrer
  //    prefill: an object with fields that will prefill the form
  //    followUpUrl: the url to redirect to after the survey is completed
  //    scrollOffset: the gap to leave between the top of the browser
  //      window and the start of a form when scrolling down. default is 80
  initialize(options={}) {
    let hasScrollOffset = options.hasOwnProperty('scrollOffset');
    this.scrollOffset = hasScrollOffset ? options.scrollOffset : this.DEFAULT_SCROLL_OFFSET;
    this.$forms = this.$('.survey__form');
    if (!MobileCheck.isMobile()) {
      this.selectizeCountry();
    }
    this.insertHiddenFields(options);
    this.prefill(options.prefill);
    this.revealFirstForm();
    this.submitFirstFormIfComplete();
    this.followUpUrl = options.followUpUrl;
    GlobalEvents.bindEvents(this);
  },

  revealFirstForm() {
    this.$('.survey__form').first().removeClass('hidden-closed');
  },

  selectizeCountry() {
    this.$('.action-form__country-selector').selectize();
  },

  // prefillValues - an object mapping form names to prefill values
  prefill(prefillValues) {
    this.prefillValues = prefillValues
    if(!_.isObject(prefillValues)) { return; }
    this.$('.survey__form input, select').each((ii, field) => {
      let $field = $(field);
      let name = $field.prop('name');
      if (prefillValues.hasOwnProperty(name)) {
        let radioOrCheck = ($field.prop('type') === 'radio' || $field.prop('type') === 'checkbox');
        if (radioOrCheck && $field.val() == prefillValues[name]) {
          $field.prop('checked', 'checked');
        } else {
          $field.val(prefillValues[name]);
        }
      }
    });
  },

  submitFirstFormIfComplete() {
    // form.serialize() returns the forms values as URL encoded params,
    // so `=&` only happens when there's a blank value in the form, eg
    // name=&weight=180, and `=` at the end means the last param is blank
    let $form = this.$forms.first();
    let serialized = $form.serialize();
    let noneEmpty = (serialized.indexOf('=&') === -1 && serialized.slice(-1) !== '=');
    let allPresent = _.every($form.find('input, select'), (el) => {
      if (this.$(el).prop('type') === 'hidden') return true;
      let name = this.$(el).prop('name');
      let outputIncludesName = (serialized.indexOf(`&${name}=`) !== -1 ||
                                serialized.indexOf(`${name}=`) === -0);
      let prefillIncludesName = (this.prefillValues[name] !== undefined);
      return outputIncludesName && prefillIncludesName;
    });
    if (noneEmpty && allPresent) $form.submit();
  },

  insertHiddenFields(options) {
    for(var ii = 0; ii < this.HIDDEN_FIELDS.length; ii++) {
      let field = this.HIDDEN_FIELDS[ii];
      if(options[field] && this.$el) {
        this.insertHiddenInput(field, options[field], this.$forms.first());
      }
    }
  },

  insertHiddenInput(name, value, element) {
    $('<input>').attr({
      type: 'hidden',
      name: name,
      value: value
    }).appendTo(element);
  },

  handleSuccess(e, data){
    ErrorDisplay.clearErrors(this.$(e.target));
    this.enableButton(e);
    this.followForm(this.$(e.target));
  },

  handleFailure(e, data) {
    ErrorDisplay.clearErrors(this.$(e.target));
    ErrorDisplay.show(e, data);
    this.enableButton(e);
  },

  handleSend(e) {
    ErrorDisplay.clearErrors(this.$(e.target));
    this.disableButton(e);
  },

  // we assume all sections are skippable, since required sections should
  // not have a visible skip button
  skipSection(e) {
    this.followForm(this.$(e.target).parents('.survey__form'));
  },

  followForm($form) {
    const $nextForm = $form.next('.survey__form');
    if ($nextForm.length) {
      this.revealForm($nextForm);
    } else {
      this.followUp();
    }
  },

  revealForm($form) {
    $form.removeClass('hidden-closed');
    let position = $form.offset().top - this.scrollOffset; // leave room for header
    $('html, body').animate({ scrollTop: position }, 500);
  },

  followUp() {
    if (this.followUpUrl) {
      this.redirectTo(this.followUpUrl);
    } else {
      window.alert('Thanks for completing our survey!');
    }
  },

  redirectTo(url) {
    window.location.href = url
  },

  disableButton(e) {
    this.$(e.target).find('.survey__button').each((ii, el) => {
      let $btn = this.$(el);
      if ($btn.data('enabled-text') === undefined) {
        $btn.data('enabled-text', $btn.text());
      }
      if (!$btn.hasClass('survey__skip-button')) {
        $btn.text(I18n.t('form.processing'))
      }
      $btn.addClass('button--disabled');
    });
  },

  enableButton(e) {
    this.$(e.target).find('.survey__button').each((ii, el) => {
      let $btn = this.$(el);
      $btn.text($btn.data('enabled-text'));
      $btn.removeClass('button--disabled');
    });
  },
});

module.exports = Survey;
<|endoftext|>


<commit_before>/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-bitwise */

((gl) => {
  gl.VueStage = Vue.extend({
    data() {
      return {
        builds: '',
        spinner: '',
      };
    },
    props: ['stage', 'svgs', 'match'],
    methods: {
      fetchBuilds(e) {
        const areaExpanded = e.currentTarget.attributes['aria-expanded'];

        if (areaExpanded && (areaExpanded.textContent === 'true')) return null;

        return this.$http.get(this.stage.dropdown_path)
          .then((response) => {
            this.builds = JSON.parse(response.body).html;
          }, () => {
            const flash = new Flash('Something went wrong on our end.');
            return flash;
          });
      },
    },
    computed: {
      buildsOrSpinner() {
        return this.builds ? this.builds : this.spinner;
      },
      dropdownClass() {
        if (this.builds) return 'js-builds-dropdown-container';
        return 'js-builds-dropdown-loading builds-dropdown-loading';
      },
      buildStatus() {
        return `Build: ${this.stage.status.label}`;
      },
      tooltip() {
        return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`;
      },
      svg() {
        const { icon } = this.stage.status;
        const stageIcon = icon.replace(/icon/i, 'stage_icon');
        return this.svgs[this.match(stageIcon)];
      },
      triggerButtonClass() {
        return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
      },
    },
    template: `

        <button
          @click='fetchBuilds($event)'
          :class="triggerButtonClass"
          :title='stage.title'
          data-placement="top"
          data-toggle="dropdown"
          type="button"
        >

        </button>

    `,
  });
})(window.gl || (window.gl = {}));
<commit_msg>keep graph displayed when build is clicked so that multiple tabs can be opened from graph<commit_after>/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-bitwise */

((gl) => {
  gl.VueStage = Vue.extend({
    data() {
      return {
        builds: '',
        spinner: '',
      };
    },
    props: {
      stage: {
        type: Object,
        required: true,
      },
      svgs: {
        type: DOMStringMap,
        required: true,
      },
      match: {
        type: Function,
        required: true,
      },
    },
    methods: {
      fetchBuilds(e) {
        if (this.builds) this.builds = '';

        const areaExpanded = e.currentTarget.attributes['aria-expanded'];

        if (areaExpanded && (areaExpanded.textContent === 'true')) return null;

        return this.$http.get(this.stage.dropdown_path)
          .then((response) => {
            this.builds = JSON.parse(response.body).html;
          }, () => {
            const flash = new Flash('Something went wrong on our end.');
            return flash;
          });
      },
      keepGraph(e) {
        e.stopPropagation();
      },
    },
    computed: {
      buildsOrSpinner() {
        return this.builds ? this.builds : this.spinner;
      },
      dropdownClass() {
        if (this.builds) return 'js-builds-dropdown-container';
        return 'js-builds-dropdown-loading builds-dropdown-loading';
      },
      buildStatus() {
        return `Build: ${this.stage.status.label}`;
      },
      tooltip() {
        return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`;
      },
      svg() {
        const { icon } = this.stage.status;
        const stageIcon = icon.replace(/icon/i, 'stage_icon');
        return this.svgs[this.match(stageIcon)];
      },
      triggerButtonClass() {
        return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
      },
    },
    template: `

        <button
          @click='fetchBuilds($event)'
          :class="triggerButtonClass"
          :title='stage.title'
          data-placement="top"
          data-toggle="dropdown"
          type="button"
        >

        </button>

    `,
  });
})(window.gl || (window.gl = {}));
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import RaisedButton from 'material-ui/RaisedButton';
import FlatButton from 'material-ui/FlatButton';
import moment from 'moment';
import TrashIcon from 'material-ui/svg-icons/action/delete-forever';
import DateRangeIcon from 'material-ui/svg-icons/action/date-range';
import EditorIcon from 'material-ui/svg-icons/editor/mode-edit';
import assign from 'lodash/assign';

import DetailCommande from 'components/DetailCommande';
import LivraisonSelector from 'containers/CommandeEdit/components/components/LivraisonSelector';
import DistributionSelected from 'containers/CommandeEdit/components/components/DistributionSelected';
import Paiement from 'containers/CommandeEdit/components/Paiement';

import {
  ajouter,
  augmenter,
  diminuer,
  supprimer,
  sauvegarder,
  annuler,
  setDistibution,
} from 'containers/CommandeEdit/actions';

import {
  selectCommande,
} from 'containers/CommandeEdit/selectors';

import {
  selectCommande as selectCommandeProxiweb,
  selectCommandeContenus,
  selectOffres,
  selectProduits,
  selectCommandeLivraisons,
} from 'containers/Commande/selectors';

import { selectPending } from 'containers/App/selectors';

// import { selectMontantBalance } from 'containers/CompteUtilisateur/selectors';

import styles from './OrderValidate.css';

class OrderValidate extends Component {
  static propTypes = {
    produitsById: PropTypes.object.isRequired,
    params: PropTypes.object.isRequired,
    offres: PropTypes.object.isRequired,
    commandeContenus: PropTypes.object.isRequired,
    commande: PropTypes.object.isRequired,
    utilisateurId: PropTypes.string.isRequired,
    livraisons: PropTypes.array.isRequired,
    commandeProxiweb: PropTypes.object.isRequired,
    balance: PropTypes.number,
    panierExpanded: PropTypes.bool.isRequired,

    sauvegarder: PropTypes.func.isRequired,
    annuler: PropTypes.func.isRequired,
    supprimer: PropTypes.func.isRequired,
    augmenter: PropTypes.func.isRequired,
    diminuer: PropTypes.func.isRequired,
    setDistibution: PropTypes.func.isRequired,
    pending: PropTypes.bool.isRequired,
  }

  static contextTypes = {
    muiTheme: PropTypes.object.isRequired,
  };

  state = {
    view: 'panier',
  }

  selectionnePlageHoraire = (plageHoraire, livraisonId) => {
    this.props.setDistibution(this.props.params.commandeId, plageHoraire, livraisonId);
    this.setState({ view: 'panier' });
  }

  showValidate = () => {
    const { commande, params, utilisateurId, pending } = this.props;
    const { commandeId } = params;
    const { palette } = this.context.muiTheme;
    let label = null;
    if (commande.modifiee) {
      label = pending ? 'Sauvegarde des modifications...' : 'Sauvegarder mes modifications';
    } else {
      label = pending ? 'Validation de la commande...' : 'Valider la commande';
    }
    return (

        <RaisedButton
          label={label}
          style={{ marginTop: 20 }}
          labelColor={commande.modifiee ? 'black' : 'white'}
          backgroundColor={commande.modifiee ? palette.warningColor : palette.primary1Color}
          onClick={() => this.props.sauvegarder(assign(commande, { commandeId, utilisateurId }))}
        />

    );
  }

  showDistribSelected = () => {
    const { commande, livraisons } = this.props;
    const livraison = livraisons.find((liv) => liv.id === commande.livraisonId);
    if (!livraison) return
Livraison manquante
;
    return (

        <DistributionSelected
          livraison={livraison}
          noPlageHoraire={commande.plageHoraire}
          className={styles.distriItem}
        />
        <FlatButton
          label="modifier"
          onClick={() => this.setState({ view: 'distribution' })}
          className={styles.distriButton}
          icon={<EditorIcon />}
          style={{ minWidth: 44 }}
        />

    );
  }

  showDistribButton = () =>
    <RaisedButton
      label="Choisissez le jour de distribution"
      icon={<DateRangeIcon />}
      style={{ marginTop: 20 }}
      onClick={() => this.setState({ view: 'distribution' })}
    />;

  showDetailsCommande = (contenus) => {
    const {
      offres,
      commande,
      params,
      produitsById,
      commandeContenus,
      panierExpanded,
    } = this.props;

    return (
      <DetailCommande
        contenus={contenus}
        offres={offres}
        produits={produitsById}
        supprimer={this.props.supprimer}
        augmenter={this.props.augmenter}
        diminuer={this.props.diminuer}
        readOnly={commande.datePaiement}
        commandeContenus={commandeContenus}
        commandeId={params.commandeId}
        panierExpanded={panierExpanded}
      />
    );
  }

  showLivraisonSelector = () => {
    const { commande, livraisons, params } = this.props;
    return (<LivraisonSelector
      livraisons={livraisons}
      plageHoraire={commande.plageHoraire}
      livraisonId={commande.livraisonId}
      selectionnePlageHoraire={this.selectionnePlageHoraire}
      params={params}
    />);
  }

  showCancel = () => {
    const { commande, pending } = this.props;
    return (

        <RaisedButton
          label={`${!pending ? 'Annuler ma ' : 'Annulation de la '}commande`}
          secondary
          style={{ marginTop: 20 }}
          onClick={() => this.props.annuler(commande.id, commande.commandeId)}
          icon={<TrashIcon />}
        />

    );
  }

  render() {
    const { commande, commandeContenus, params, offres, balance, commandeProxiweb, pending } = this.props;
    const { view } = this.state;
    const contenusCommande = commande.contenus.map((contenu) =>
      // quand le contenu vient d'être ajouté, contenu est un objet sans id
      // quand il s'agit d'une commande depuis Bd, il n'y a que l'id -> commandeContenus[id]
      (typeof contenu === 'object' ? contenu : commandeContenus[contenu])
    );

    return (

      { view === 'distribution' ? this.showLivraisonSelector() : this.showDetailsCommande(contenusCommande) }
      { view === 'panier' && commande.livraisonId && this.showDistribSelected() }

{view !== 'distribution' && !commande.livraisonId && commande.contenus.length > 0 && this.showDistribButton()}

      {view === 'panier' && commande.livraisonId && (!commande.id || commande.modifiee) && this.showValidate()}
      {view === 'panier' && !commande.dateLivraison && commande.id && !commande.modifiee && this.showCancel()}
      {commande.id && balance !== null && (
        <Paiement
          contenus={contenusCommande}
          commandeContenus={commandeContenus}
          commandeId={params.commandeId}
          balance={balance}
          offres={offres}
          pending={pending}
          dateLimite={moment(commandeProxiweb.dateCommande).format('LLLL')}
        />
      )}

);
  }
}

const mapStateToProps = createStructuredSelector({
  commande: selectCommande(), // commande courante en cours d'édition
  commandeProxiweb: selectCommandeProxiweb(),
  offres: selectOffres(),
  commandeContenus: selectCommandeContenus(),
  produitsById: selectProduits(),
  // balance: selectMontantBalance(),
  livraisons: selectCommandeLivraisons(),
  pending: selectPending(),
});

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    ajouter: (commandeId, offre) => dispatch(ajouter(commandeId, offre)),
    augmenter: (commandeId, offreId) => dispatch(augmenter(commandeId, offreId)),
    diminuer: (commandeId, offreId) => dispatch(diminuer(commandeId, offreId)),
    supprimer: (offreId) => dispatch(supprimer(offreId)),
    sauvegarder: (datas) => dispatch(sauvegarder(datas)),
    annuler: (id, commandeId) => dispatch(annuler(id, commandeId)),
    setDistibution: (commandeId, livraisonId, plageHoraire) => dispatch(setDistibution(commandeId, livraisonId, plageHoraire)),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(OrderValidate);
<commit_msg>Ne pas afficher le paiement quand on choisit la distribution<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import RaisedButton from 'material-ui/RaisedButton';
import FlatButton from 'material-ui/FlatButton';
import moment from 'moment';
import TrashIcon from 'material-ui/svg-icons/action/delete-forever';
import DateRangeIcon from 'material-ui/svg-icons/action/date-range';
import EditorIcon from 'material-ui/svg-icons/editor/mode-edit';
import assign from 'lodash/assign';

import DetailCommande from 'components/DetailCommande';
import LivraisonSelector from 'containers/CommandeEdit/components/components/LivraisonSelector';
import DistributionSelected from 'containers/CommandeEdit/components/components/DistributionSelected';
import Paiement from 'containers/CommandeEdit/components/Paiement';

import {
  ajouter,
  augmenter,
  diminuer,
  supprimer,
  sauvegarder,
  annuler,
  setDistibution,
} from 'containers/CommandeEdit/actions';

import {
  selectCommande,
} from 'containers/CommandeEdit/selectors';

import {
  selectCommande as selectCommandeProxiweb,
  selectCommandeContenus,
  selectOffres,
  selectProduits,
  selectCommandeLivraisons,
} from 'containers/Commande/selectors';

import { selectPending } from 'containers/App/selectors';

// import { selectMontantBalance } from 'containers/CompteUtilisateur/selectors';

import styles from './OrderValidate.css';

class OrderValidate extends Component {
  static propTypes = {
    produitsById: PropTypes.object.isRequired,
    params: PropTypes.object.isRequired,
    offres: PropTypes.object.isRequired,
    commandeContenus: PropTypes.object.isRequired,
    commande: PropTypes.object.isRequired,
    utilisateurId: PropTypes.string.isRequired,
    livraisons: PropTypes.array.isRequired,
    commandeProxiweb: PropTypes.object.isRequired,
    balance: PropTypes.number,
    panierExpanded: PropTypes.bool.isRequired,

    sauvegarder: PropTypes.func.isRequired,
    annuler: PropTypes.func.isRequired,
    supprimer: PropTypes.func.isRequired,
    augmenter: PropTypes.func.isRequired,
    diminuer: PropTypes.func.isRequired,
    setDistibution: PropTypes.func.isRequired,
    pending: PropTypes.bool.isRequired,
  }

  static contextTypes = {
    muiTheme: PropTypes.object.isRequired,
  };

  state = {
    view: 'panier',
  }

  selectionnePlageHoraire = (plageHoraire, livraisonId) => {
    this.props.setDistibution(this.props.params.commandeId, plageHoraire, livraisonId);
    this.setState({ view: 'panier' });
  }

  showValidate = () => {
    const { commande, params, utilisateurId, pending } = this.props;
    const { commandeId } = params;
    const { palette } = this.context.muiTheme;
    let label = null;
    if (commande.modifiee) {
      label = pending ? 'Sauvegarde des modifications...' : 'Sauvegarder mes modifications';
    } else {
      label = pending ? 'Validation de la commande...' : 'Valider la commande';
    }
    return (

        <RaisedButton
          label={label}
          style={{ marginTop: 20 }}
          labelColor={commande.modifiee ? 'black' : 'white'}
          backgroundColor={commande.modifiee ? palette.warningColor : palette.primary1Color}
          onClick={() => this.props.sauvegarder(assign(commande, { commandeId, utilisateurId }))}
        />

    );
  }

  showDistribSelected = () => {
    const { commande, livraisons } = this.props;
    const livraison = livraisons.find((liv) => liv.id === commande.livraisonId);
    if (!livraison) return
Livraison manquante
;
    return (

        <DistributionSelected
          livraison={livraison}
          noPlageHoraire={commande.plageHoraire}
          className={styles.distriItem}
        />
        <FlatButton
          label="modifier"
          onClick={() => this.setState({ view: 'distribution' })}
          className={styles.distriButton}
          icon={<EditorIcon />}
          style={{ minWidth: 44 }}
        />

    );
  }

  showDistribButton = () =>
    <RaisedButton
      label="Choisissez le jour de distribution"
      icon={<DateRangeIcon />}
      style={{ marginTop: 20 }}
      onClick={() => this.setState({ view: 'distribution' })}
    />;

  showDetailsCommande = (contenus) => {
    const {
      offres,
      commande,
      params,
      produitsById,
      commandeContenus,
      panierExpanded,
    } = this.props;

    return (
      <DetailCommande
        contenus={contenus}
        offres={offres}
        produits={produitsById}
        supprimer={this.props.supprimer}
        augmenter={this.props.augmenter}
        diminuer={this.props.diminuer}
        readOnly={commande.datePaiement}
        commandeContenus={commandeContenus}
        commandeId={params.commandeId}
        panierExpanded={panierExpanded}
      />
    );
  }

  showLivraisonSelector = () => {
    const { commande, livraisons, params } = this.props;
    return (<LivraisonSelector
      livraisons={livraisons}
      plageHoraire={commande.plageHoraire}
      livraisonId={commande.livraisonId}
      selectionnePlageHoraire={this.selectionnePlageHoraire}
      params={params}
    />);
  }

  showCancel = () => {
    const { commande, pending } = this.props;
    return (

        <RaisedButton
          label={`${!pending ? 'Annuler ma ' : 'Annulation de la '}commande`}
          secondary
          style={{ marginTop: 20 }}
          onClick={() => this.props.annuler(commande.id, commande.commandeId)}
          icon={<TrashIcon />}
        />

    );
  }

  render() {
    const { commande, commandeContenus, params, offres, balance, commandeProxiweb, pending } = this.props;
    const { view } = this.state;
    const contenusCommande = commande.contenus.map((contenu) =>
      // quand le contenu vient d'être ajouté, contenu est un objet sans id
      // quand il s'agit d'une commande depuis Bd, il n'y a que l'id -> commandeContenus[id]
      (typeof contenu === 'object' ? contenu : commandeContenus[contenu])
    );

    return (

      { view === 'distribution' ? this.showLivraisonSelector() : this.showDetailsCommande(contenusCommande) }
      { view === 'panier' && commande.livraisonId && this.showDistribSelected() }

{view !== 'distribution' && !commande.livraisonId && commande.contenus.length > 0 && this.showDistribButton()}

      {view === 'panier' && commande.livraisonId && (!commande.id || commande.modifiee) && this.showValidate()}
      {view === 'panier' && !commande.dateLivraison && commande.id && !commande.modifiee && this.showCancel()}
      {commande.id && balance !== null && view === 'panier' && (
        <Paiement
          contenus={contenusCommande}
          commandeContenus={commandeContenus}
          commandeId={params.commandeId}
          balance={balance}
          offres={offres}
          pending={pending}
          dateLimite={moment(commandeProxiweb.dateCommande).format('LLLL')}
        />
      )}

);
  }
}

const mapStateToProps = createStructuredSelector({
  commande: selectCommande(), // commande courante en cours d'édition
  commandeProxiweb: selectCommandeProxiweb(),
  offres: selectOffres(),
  commandeContenus: selectCommandeContenus(),
  produitsById: selectProduits(),
  // balance: selectMontantBalance(),
  livraisons: selectCommandeLivraisons(),
  pending: selectPending(),
});

function mapDispatchToProps(dispatch) {
  return {
    dispatch,
    ajouter: (commandeId, offre) => dispatch(ajouter(commandeId, offre)),
    augmenter: (commandeId, offreId) => dispatch(augmenter(commandeId, offreId)),
    diminuer: (commandeId, offreId) => dispatch(diminuer(commandeId, offreId)),
    supprimer: (offreId) => dispatch(supprimer(offreId)),
    sauvegarder: (datas) => dispatch(sauvegarder(datas)),
    annuler: (id, commandeId) => dispatch(annuler(id, commandeId)),
    setDistibution: (commandeId, livraisonId, plageHoraire) => dispatch(setDistibution(commandeId, livraisonId, plageHoraire)),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(OrderValidate);
<|endoftext|>


<commit_before>import React, { useMemo, useState, useCallback, useEffect } from 'react';
import { Box, Table, Flex, Avatar, TextInput, Icon } from '@rocket.chat/fuselage';
import { useMediaQuery } from '@rocket.chat/fuselage-hooks';

import { GenericTable, Th } from '../../../../components/GenericTable';
import { useTranslation } from '../../../../../../../client/contexts/TranslationContext';
import { useRoute } from '../../../../../../../client/contexts/RouterContext';
import { usePermission } from '../../../../../../../client/contexts/AuthorizationContext';
import { useQuery } from '../hooks';
import { roomTypes } from '../../../../../../utils/client';
import { useEndpointData } from '../../../../../../../client/hooks/useEndpointData';
import { useFormatDate } from '../../../../../../../client/hooks/useFormatDate';

const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' };

const FilterByText = ({ setFilter, ...props }) => {
	const t = useTranslation();
	const [text, setText] = useState('');
	const handleChange = useCallback((event) => setText(event.currentTarget.value), []);

	useEffect(() => {
		setFilter({ text });
	}, [text]);

	return <Box mb='x16' is='form' display='flex' flexDirection='column' {...props}>
		<TextInput flexShrink={0} placeholder={t('Search_Channels')} addon={<Icon name='magnifier' size='x20'/>} onChange={handleChange} value={text} />
	</Box>;
};

export function UserTab({
	workspace = 'local',
}) {
	const [params, setParams] = useState({ current: 0, itemsPerPage: 25 });
	const [sort, setSort] = useState(['name', 'asc']);
	const canViewFullOtherUserInfo = usePermission('view-full-other-user-info');
	const t = useTranslation();

	const federation = workspace === 'external';

	const query = useQuery(params, sort, 'users', workspace);

	const mediaQuery = useMediaQuery('(min-width: 1024px)');

	const onHeaderClick = (id) => {
		const [sortBy, sortDirection] = sort;

		if (sortBy === id) {
			setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
			return;
		}
		setSort([id, 'asc']);
	};

	const header = useMemo(() => [
		{t('Name')}	,
		mediaQuery && canViewFullOtherUserInfo && {t('Email')}	,
		federation && {t('Domain')}	,
		mediaQuery && {t('Joined_at')}	,
	].filter(Boolean), [sort, federation, canViewFullOtherUserInfo, mediaQuery]);

	const directRoute = useRoute('direct');

	const canViewOutsideRoom = usePermission('view-outside-room');
	const canViewDM = usePermission('view-d-room');

	const data = (canViewOutsideRoom && canViewDM && useEndpointData('directory', query)) || { result: [] };

	const onClick = useMemo(() => (username) => (e) => {
		if (e.type === 'click' || e.key === 'Enter') {
			directRoute.push({ rid: username });
		}
	}, [directRoute]);

	const formatDate = useFormatDate();

	const renderRow = useCallback(({ createdAt, emails, _id, username, name, domain, bio }) => {
		const avatarUrl = roomTypes.getConfig('d').getAvatarPath({ name: username || name, type: 'd', _id });

		return

				<Flex.Container>
					<Box>
						<Flex.Item>
							<Avatar size='x40' title={username} url={avatarUrl} />
						</Flex.Item>
						<Box style={style} grow={1} mi='x8'>
							<Box display='flex'>
								<Box fontScale='p2' style={style}>{name || username}</Box> <Box mi='x4'/> <Box fontScale='p1' color='hint' style={style}>{username}</Box>
							</Box>
							<Box fontScale='p1' color='hint' style={style}> {bio} </Box>
						</Box>
					</Box>
				</Flex.Container>

			{mediaQuery && canViewFullOtherUserInfo
			&&

				{emails && emails[0].address}

}
			{federation
		&&

			{domain}

}
			{mediaQuery &&

				{formatDate(createdAt)}

}

;
	}, [mediaQuery, federation, canViewFullOtherUserInfo]);

	return <GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data.result} total={data.total} setParams={setParams} />;
}
<commit_msg>[FIX] Directory search user placeholder (#17652)<commit_after>import React, { useMemo, useState, useCallback, useEffect } from 'react';
import { Box, Table, Flex, Avatar, TextInput, Icon } from '@rocket.chat/fuselage';
import { useMediaQuery } from '@rocket.chat/fuselage-hooks';

import { GenericTable, Th } from '../../../../components/GenericTable';
import { useTranslation } from '../../../../../../../client/contexts/TranslationContext';
import { useRoute } from '../../../../../../../client/contexts/RouterContext';
import { usePermission } from '../../../../../../../client/contexts/AuthorizationContext';
import { useQuery } from '../hooks';
import { roomTypes } from '../../../../../../utils/client';
import { useEndpointData } from '../../../../../../../client/hooks/useEndpointData';
import { useFormatDate } from '../../../../../../../client/hooks/useFormatDate';

const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' };

const FilterByText = ({ setFilter, ...props }) => {
	const t = useTranslation();
	const [text, setText] = useState('');
	const handleChange = useCallback((event) => setText(event.currentTarget.value), []);

	useEffect(() => {
		setFilter({ text });
	}, [text]);

	return <Box mb='x16' is='form' display='flex' flexDirection='column' {...props}>
		<TextInput flexShrink={0} placeholder={t('Search_Users')} addon={<Icon name='magnifier' size='x20'/>} onChange={handleChange} value={text} />
	</Box>;
};

export function UserTab({
	workspace = 'local',
}) {
	const [params, setParams] = useState({ current: 0, itemsPerPage: 25 });
	const [sort, setSort] = useState(['name', 'asc']);
	const canViewFullOtherUserInfo = usePermission('view-full-other-user-info');
	const t = useTranslation();

	const federation = workspace === 'external';

	const query = useQuery(params, sort, 'users', workspace);

	const mediaQuery = useMediaQuery('(min-width: 1024px)');

	const onHeaderClick = (id) => {
		const [sortBy, sortDirection] = sort;

		if (sortBy === id) {
			setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
			return;
		}
		setSort([id, 'asc']);
	};

	const header = useMemo(() => [
		{t('Name')}	,
		mediaQuery && canViewFullOtherUserInfo && {t('Email')}	,
		federation && {t('Domain')}	,
		mediaQuery && {t('Joined_at')}	,
	].filter(Boolean), [sort, federation, canViewFullOtherUserInfo, mediaQuery]);

	const directRoute = useRoute('direct');

	const canViewOutsideRoom = usePermission('view-outside-room');
	const canViewDM = usePermission('view-d-room');

	const data = (canViewOutsideRoom && canViewDM && useEndpointData('directory', query)) || { result: [] };

	const onClick = useMemo(() => (username) => (e) => {
		if (e.type === 'click' || e.key === 'Enter') {
			directRoute.push({ rid: username });
		}
	}, [directRoute]);

	const formatDate = useFormatDate();

	const renderRow = useCallback(({ createdAt, emails, _id, username, name, domain, bio }) => {
		const avatarUrl = roomTypes.getConfig('d').getAvatarPath({ name: username || name, type: 'd', _id });

		return

				<Flex.Container>
					<Box>
						<Flex.Item>
							<Avatar size='x40' title={username} url={avatarUrl} />
						</Flex.Item>
						<Box style={style} grow={1} mi='x8'>
							<Box display='flex'>
								<Box fontScale='p2' style={style}>{name || username}</Box> <Box mi='x4'/> <Box fontScale='p1' color='hint' style={style}>{username}</Box>
							</Box>
							<Box fontScale='p1' color='hint' style={style}> {bio} </Box>
						</Box>
					</Box>
				</Flex.Container>

			{mediaQuery && canViewFullOtherUserInfo
			&&

				{emails && emails[0].address}

}
			{federation
		&&

			{domain}

}
			{mediaQuery &&

				{formatDate(createdAt)}

}

;
	}, [mediaQuery, federation, canViewFullOtherUserInfo]);

	return <GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data.result} total={data.total} setParams={setParams} />;
}
<|endoftext|>


<commit_before>/**
 * The slide menu widget
 *
 * @class Widgets.com.chariti.slideMenu
 * @uses core
 */
var APP = require("core"),

var sections = [];
var tabs = [];

/**
 * Initializes the slide menu
 * @param {Object} _params
 * @param {Object} _params.tabs The tab items to show in the side menu as defined by the JSON configuration file
 */
$.init = function(_params) {
	$.tabs = [];

	// Add the Settings tab
	_params.tabs.push({
		id: "settings",
		image: "/icons/white/settings.png",
		title: "Settings"
	});

	//creates a TableViewSection for each tab with a menuHeader property
	buildSections(_params.tabs);

	if(sections.length > 0) {
		var currentSection = -1;
	}

	for(var i = 0; i < _params.tabs.length; i++) {
		// Iterates through the created sections
		if(_params.tabs[i].menuHeader) {
			currentSection++;
		}

		var tab = Ti.UI.createTableViewRow({
			id: _params.tabs[i].id,
			height: "47dp",
			backgroundcolor: "#111",
			backgroundSelectedColor: "#222",
			selectedBackgroundColor: "#222"
		});

		var label = Ti.UI.createLabel({
			text: _params.tabs[i].title,
			top: "0dp",
			left: "47dp",
			right: "13dp",
			height: "46dp",
			font: {
				fontSize: "16dp",
				fontFamily: "HelveticaNeue-Light"
			},
			color: "#FFF",
			touchEnabled: false
		});

		if(_params.tabs[i].image) {
			var icon = Ti.UI.createImageView({
				image: _params.tabs[i].image,
				width: "21dp",
				height: "21dp",
				top: "13dp",
				left: "13dp",
				touchEnabled: false,
				preventDefaultImage: true
			});

			tab.add(icon);
		}

		tab.add(label);

		if(sections.length > 0) {
			sections[currentSection].add(tab);

			//If the last tab has been created and added to a section or
			//the next tab is a new header, append the current section to the TableView
			if(i + 1 !== _params.tabs.length) {
				if(_params.tabs[i+1].menuHeader) {
					$.Tabs.appendSection(sections[currentSection]);
				}
			} else {
				$.Tabs.appendSection(sections[currentSection]);
			}
		} else {
			tabs.push(tab);
		}
	}

	if(tabs.length > 0) {
		$.Tabs.setData(tabs);
	}
};

/**
 * Builds out the table sections
 * @param {Object} _tabs The tab items to show in the side menu
 * @private
 */
function buildSections(_tabs) {
	for(var i = 0; i < _tabs.length; i++) {
		// Assigns special menuHeader styling
		if(_tabs[i].menuHeader) {
			var section = Ti.UI.createTableViewSection();

			var header = Ti.UI.createView({
				top: "0dp",
				height: "20dp",
				width: Ti.UI.FILL,
				backgroundColor: APP.Settings.colors.primary
			});

			var headerText = Ti.UI.createLabel({
				text: _tabs[i].menuHeader,
				top: "2dp",
				left: "13dp",
				font: {
					fontSize: "12dp",
					fontWeight: "HelveticaNeue-Light"
				},
				color: APP.Settings.colors.theme == "dark" ? "#FFF" : "#000",
				touchEnabled: false,
				verticalAlignment: Titanium.UI.TEXT_VERTICAL_ALIGNMENT_CENTER,
				isHeader: true
			});

			header.add(headerText);

			section.headerView = header;

			sections.push(section);
		}
	}
};

/**
 * Clears all items from the side menu
 */
$.clear = function() {
	$.Tabs.removeAllChildren();
};

/**
 * Sets the indexed item as active
 * @param {Object} _index The index of the item to show as active
 */
$.setIndex = function(_index) {
	$.Tabs.selectRow(_index);
};

$.Tabs.addEventListener("click", function(_event) {
	if(typeof _event.index !== "undefined") {
		$.setIndex(_event.index);
	}
});

// Move the UI down if iOS7+
if(OS_IOS && APP.Device.versionMajor >= 7) {
	$.Tabs.top = "20dp";
}<commit_msg>Beautifying and resolving issues from merges<commit_after>/**
 * The slide menu widget
 *
 * @class Widgets.com.chariti.slideMenu
 * @uses core
 */
var APP = require("core");

var sections = [];
var tabs = [];

/**
 * Initializes the slide menu
 * @param {Object} _params
 * @param {Object} _params.tabs The tab items to show in the side menu as defined by the JSON configuration file
 */
$.init = function(_params) {
	$.tabs = [];

	// Add the Settings tab
	_params.tabs.push({
		id: "settings",
		image: "/icons/white/settings.png",
		title: "Settings"
	});

	//creates a TableViewSection for each tab with a menuHeader property
	buildSections(_params.tabs);

	if(sections.length > 0) {
		var currentSection = -1;
	}

	for(var i = 0; i < _params.tabs.length; i++) {
		// Iterates through the created sections
		if(_params.tabs[i].menuHeader) {
			currentSection++;
		}

		var tab = Ti.UI.createTableViewRow({
			id: _params.tabs[i].id,
			height: "47dp",
			backgroundcolor: "#111",
			backgroundSelectedColor: "#222",
			selectedBackgroundColor: "#222"
		});

		var label = Ti.UI.createLabel({
			text: _params.tabs[i].title,
			top: "0dp",
			left: "47dp",
			right: "13dp",
			height: "46dp",
			font: {
				fontSize: "16dp",
				fontFamily: "HelveticaNeue-Light"
			},
			color: "#FFF",
			touchEnabled: false
		});

		if(_params.tabs[i].image) {
			var icon = Ti.UI.createImageView({
				image: _params.tabs[i].image,
				width: "21dp",
				height: "21dp",
				top: "13dp",
				left: "13dp",
				touchEnabled: false,
				preventDefaultImage: true
			});

			tab.add(icon);
		}

		tab.add(label);

		if(sections.length > 0) {
			sections[currentSection].add(tab);

			//If the last tab has been created and added to a section or
			//the next tab is a new header, append the current section to the TableView
			if(i + 1 !== _params.tabs.length) {
				if(_params.tabs[i + 1].menuHeader) {
					$.Tabs.appendSection(sections[currentSection]);
				}
			} else {
				$.Tabs.appendSection(sections[currentSection]);
			}
		} else {
			tabs.push(tab);
		}
	}

	if(tabs.length > 0) {
		$.Tabs.setData(tabs);
	}
};

/**
 * Builds out the table sections
 * @param {Object} _tabs The tab items to show in the side menu
 * @private
 */
function buildSections(_tabs) {
	for(var i = 0; i < _tabs.length; i++) {
		// Assigns special menuHeader styling
		if(_tabs[i].menuHeader) {
			var section = Ti.UI.createTableViewSection();

			var header = Ti.UI.createView({
				top: "0dp",
				height: "20dp",
				width: Ti.UI.FILL,
				backgroundColor: APP.Settings.colors.primary
			});

			var headerText = Ti.UI.createLabel({
				text: _tabs[i].menuHeader,
				top: "2dp",
				left: "13dp",
				font: {
					fontSize: "12dp",
					fontWeight: "HelveticaNeue-Light"
				},
				color: APP.Settings.colors.theme == "dark" ? "#FFF" : "#000",
				touchEnabled: false,
				verticalAlignment: Titanium.UI.TEXT_VERTICAL_ALIGNMENT_CENTER,
				isHeader: true
			});

			header.add(headerText);

			section.headerView = header;

			sections.push(section);
		}
	}
};

/**
 * Clears all items from the side menu
 */
$.clear = function() {
	$.Tabs.removeAllChildren();
};

/**
 * Sets the indexed item as active
 * @param {Object} _index The index of the item to show as active
 */
$.setIndex = function(_index) {
	$.Tabs.selectRow(_index);
};

$.Tabs.addEventListener("click", function(_event) {
	if(typeof _event.index !== "undefined") {
		$.setIndex(_event.index);
	}
});

// Move the UI down if iOS7+
if(OS_IOS && APP.Device.versionMajor >= 7) {
	$.Tabs.top = "20dp";
}<|endoftext|>