<commit_before>/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var Connection = require('./connection');
var utils = require('./utils');

var Customizer = require('./customizer');

var optionKeys = [
    'connection',
    'maxRounds',
    'triesPerValue',
    'timeoutPerValue',
    'masterTimeout',
];

var ConnectionCustomizer = Customizer.extend(/** @lends ConnectionCustomizer# */ {

    /**
     * The connection to use for transfer of the configuration values.
     * @type {Connection}
     */
    connection: null,

    /**
     * Maximum number of optimization rounds for {@link transceiveConfiguration}.
     * @type {number}
     * @default 10
     */
    maxRounds: 10,

    /**
     * Amount of retries to transceive one value.
     * Between two tries the VBus is released and then re-acquired.
     * @type {number}
     * @default 2
     */
    triesPerValue: 2,

    /**
     * Timeout in milliseconds after which the transceive times out.
     * @type {number}
     * @default 30000
     */
    timeoutPerValue: 30000,

    /**
     * Interval in milliseconds in which
     * the VBus master is contacted to reissue the VBus clearance.
     * @type {number}
     * @default 8000
     */
    masterTimeout: 8000,

    /**
     * Constructs a new ConnectionCustomizer instance and optionally initializes its
     * members with the given values.
     *
     * @constructs
     * @augments Customizer
     * @param {object} [options] Initialization values for this instance's members
     * @param {number} [options.connection] {@link ConnectionCustomizer#connection}
     * @param {number} [options.maxRounds] {@link ConnectionCustomizer#maxRounds}
     * @param {number} [options.triesPerValue] {@link ConnectionCustomizer#triesPerValue}
     * @param {number} [options.timeoutPerValue] {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} [options.masterTimeout] {@link ConnectionCustomizer#masterTimeout}
     *
     * @classdesc
     * A ConnectionCustomizer uses an established connection to a device
     * to transfer sets of configuration values over it.
     */
    constructor: function(options) {
        Customizer.apply(this, arguments);

        _.extend(this, _.pick(options, optionKeys));
    },

    loadConfiguration: function() {
        var _this = this;

        var options = {
            action: 'get',
        };

        var callback = function(config, round) {
            if (round === 1) {
                return _this._getInitialConfiguration(config);
            } else {
                return _this._optimizeConfiguration(config);
            }
        };

        return this.transceiveConfiguration(options, callback);
    },

    saveConfiguration: function(configuration) {
        var _this = this;

        var options = {
            action: 'set',
            actionOptions: {
                save: true,
            },
        };

        var callback = function(config, round) {
            if (round === 1) {
                return _this._getInitialConfiguration(config);
            } else {
                return _this._optimizeConfiguration(config);
            }
        };

        return this.transceiveConfiguration(options, callback);
    },

    /**
     * Transceives a controller configuration set, handling timeouts, retries etc.
     *
     * @param {object} options Options
     * @param {number} [options.maxRounds] {@link ConnectionCustomizer#maxRounds}
     * @param {number} [options.triesPerValue] {@link ConnectionCustomizer#triesPerValue}
     * @param {number} [options.timeoutPerValue] {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} [options.masterTimeout] {@link ConnectionCustomizer#masterTimeout}
     * @param {number} options.action Action to perform, can be `'get'` or `'set'`.
     * @param {number} [options.actionOptions] Options object to forward to the action to perform.
     * @return {object} Promise that resolves to the configuration or `null` on timeout.
     */
    transceiveConfiguration: function(options, callback) {
        var _this = this;

        if (_.isFunction(options)) {
            callback = options;
            options = null;
        }

        options = _.defaults({}, options, {
            maxRounds: this.maxRounds,
            triesPerValue: this.triesPerValue,
            timeoutPerValue: this.timeoutPerValue,
            masterTimeout: this.masterTimeout,
            action: null,
            actionOptions: null,
        });

        var connection = this.connection;
        var address = this.deviceAddress;

        return utils.cancelablePromise(function(resolve, reject, notify, checkCanceled) {
            var config = null;

            var state = {
                masterAddress: null,
                masterLastContacted: null,
            };

            var round = 0;

            var reportProgress = function(progress) {
                if (_.isString(progress)) {
                    progress = {
                        message: progress,
                    };
                }

                _.extend(progress, {
                    round: round,
                });

                notify(progress);
            };

            var nextRound = function() {
                if (round < options.maxRounds) {
                    round++;

                    Q.fcall(checkCanceled).then(function() {
                        reportProgress('OPTIMIZING_VALUES');

                        return callback(config, round);
                    }).then(checkCanceled).then(function(newConfig) {
                        config = newConfig;

                        var pendingValues = _.filter(config, function(value) {
                            return value.pending;
                        });

                        var index = 0;

                        var nextValue = function() {
                            if (index < pendingValues.length) {
                                var valueInfo = pendingValues [index++];

                                Q.fcall(checkCanceled).then(function() {
                                    return _this.transceiveValue(valueInfo.valueIndex, valueInfo.value, {
                                        triesPerValue: options.triesPerValue,
                                        timeoutPerValue: options.timeoutPerValue,
                                        action: options.action,
                                        actionOptions: options.actionOptions,
                                    }, state).progress(function(progress) {
                                        reportProgress(progress);
                                    });
                                }).then(function(datagram) {
                                    valueInfo.pending = false;
                                    valueInfo.transceived = !!datagram;

                                    if (datagram) {
                                        valueInfo.value = datagram.value;
                                    }

                                    nextValue();
                                }).done();
                            } else {
                                nextRound(config);
                            }
                        };

                        if (pendingValues.length > 0) {
                            process.nextTick(nextValue);
                        } else {
                            Q.fcall(function() {
                                if (state.masterLastContacted !== null) {
                                    reportProgress('RELEASING_BUS');

                                    return connection.releaseBus(address);
                                }
                            }).then(function() {
                                resolve(config);
                            }).done();
                        }
                    }).fail(reject).done();
                } else {
                    resolve(null);
                }
            };

            process.nextTick(nextRound);
        });
    },

    /**
     * Transceive a controller value over this connection, handling
     * timeouts, retries etc.
     *
     * @param {number} valueIndex Value index
     * @param {number} value Value
     * @param {object} options Options
     * @param {number} options.triesPerValue {@link ConnectionCustomizer#triesPerValue}
     * @param {number} options.timeoutPerValue {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} options.masterTimeout {@link ConnectionCustomizer#masterTimeout}
     * @param {object} state State to share between multiple calls to this method.
     * @returns {object} Promise that resolves with the datagram received or `null` on timeout.
     */
    transceiveValue: function(valueIndex, value, options, state) {
        if (state === undefined) {
            state = {};
        }

        options = _.defaults({}, options, {
            triesPerValue: this.triesPerValue,
            timeoutPerValue: this.timeoutPerValue,
            masterTimeout: this.masterTimeout,
            action: null,
            actionOptions: null,
        });

        state = _.defaults(state, {
            masterAddress: this.deviceAddress,
            masterLastContacted: Date.now(),
        });

        var connection = this.connection;
        var address = this.deviceAddress;

        return utils.cancelablePromise(function(resolve, reject, notify, checkCanceled) {
            var timer, onConnectionState;

            var done = function(err, result) {
                if (timer) {
                    clearTimeout(timer);
                    timer = null;
                }

                if (onConnectionState) {
                    connection.removeListener('connectionState', onConnectionState);
                }

                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            };

            var check = function(result) {
                return Q.fcall(function() {
                    return checkCanceled();
                }).then(function() {
                    return utils.promise(function(resolve, reject) {
                        if (connection.connectionState === Connection.STATE_DISCONNECTED) {
                            reject(new Error('Disconnected'));
                        } else if (connection.connectionState !== Connection.STATE_CONNECTED) {
                            onConnectionState = function(state) {
                                if (state === Connection.STATE_DISCONNECTED) {
                                    reject(new Error('Disconnected'));
                                } else if (state === Connection.STATE_CONNECTED) {
                                    connection.removeListener('connectionState', onConnectionState);

                                    onConnectionState = null;

                                    resolve();
                                }
                            };

                            connection.on('connectionState', onConnectionState);
                        } else {
                            resolve();
                        }
                    });
                });
            };

            var tries = 0;

            var reportProgress = function(message) {
                notify({
                    message: message,
                    tries: tries,
                });
            };

            var nextTry = function() {
                if (tries < options.triesPerValue) {
                    tries++;

                    Q.fcall(check).then(function() {
                        if ((tries > 1) && (state.masterLastContacted !== null)) {
                            reportProgress('RELEASING_BUS');

                            state.masterLastContacted = null;

                            return connection.releaseBus(state.masterAddress);
                        }
                    }).then(check).then(function() {
                        if (state.masterLastContacted === null) {
                            reportProgress('WAITING_FOR_FREE_BUS');

                            return connection.waitForFreeBus().then(function(datagram) { // TODO: optional timeout?
                                if (datagram) {
                                    state.masterAddress = datagram.sourceAddress;
                                } else {
                                    state.masterAddress = null;
                                }
                            });
                        }
                    }).then(check).then(function() {
                        var contactMaster;
                        if (state.masterAddress === null) {
                            contactMaster = false;
                        } else if (state.masterAddress === address) {
                            contactMaster = false;
                        } else if (state.masterLastContacted === null) {
                            contactMaster = true;
                        } else if ((Date.now() - state.masterLastContacted) >= options.masterTimeout) {
                            contactMaster = true;
                        } else {
                            contactMaster = false;
                        }
                        if (contactMaster) {
                            reportProgress('CONTACTING_MASTER');

                            state.masterLastContacted = Date.now();

                            return connection.getValueById(state.masterAddress, 0, {
                                timeout: 500,
                                tries: 1,
                            });
                        }
                    }).then(check).then(function() {
                        if (state.masterAddress === address) {
                            state.masterLastContacted = Date.now();
                        }

                        if (options.action === 'get') {
                            reportProgress('GETTING_VALUE');

                            return connection.getValueById(address, valueIndex, options.actionOptions);
                        } else if (options.action === 'set') {
                            reportProgress('SETTING_VALUE');

                            return connection.setValueById(address, valueIndex, value, options.actionOptions);
                        } else {
                            throw new Error('Unknown action "' + options.action + '"');
                        }
                    }).then(function(datagram) {
                        if (datagram) {
                            done(null, datagram);
                        } else {
                            return nextTry();
                        }
                    }).fail(function(reason) {
                        done(reason);
                    }).done();
                } else {
                    done(null, null);
                }
            };

            var onTimeout = function() {
                done(null, null);
            };

            process.nextTick(nextTry);
            timer = setTimeout(onTimeout, options.timeoutPerValue);
        });
    }
});

module.exports = ConnectionCustomizer;
<commit_msg>Refactor ConnectionCustomizer to use new helper.<commit_after>/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var Connection = require('./connection');
var utils = require('./utils');

var Customizer = require('./customizer');

var optionKeys = [
    'connection',
    'maxRounds',
    'triesPerValue',
    'timeoutPerValue',
    'masterTimeout',
];

var ConnectionCustomizer = Customizer.extend(/** @lends ConnectionCustomizer# */ {

    /**
     * The connection to use for transfer of the configuration values.
     * @type {Connection}
     */
    connection: null,

    /**
     * Maximum number of optimization rounds for {@link transceiveConfiguration}.
     * @type {number}
     * @default 10
     */
    maxRounds: 10,

    /**
     * Amount of retries to transceive one value.
     * Between two tries the VBus is released and then re-acquired.
     * @type {number}
     * @default 2
     */
    triesPerValue: 2,

    /**
     * Timeout in milliseconds after which the transceive times out.
     * @type {number}
     * @default 30000
     */
    timeoutPerValue: 30000,

    /**
     * Interval in milliseconds in which
     * the VBus master is contacted to reissue the VBus clearance.
     * @type {number}
     * @default 8000
     */
    masterTimeout: 8000,

    /**
     * Constructs a new ConnectionCustomizer instance and optionally initializes its
     * members with the given values.
     *
     * @constructs
     * @augments Customizer
     * @param {object} [options] Initialization values for this instance's members
     * @param {number} [options.connection] {@link ConnectionCustomizer#connection}
     * @param {number} [options.maxRounds] {@link ConnectionCustomizer#maxRounds}
     * @param {number} [options.triesPerValue] {@link ConnectionCustomizer#triesPerValue}
     * @param {number} [options.timeoutPerValue] {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} [options.masterTimeout] {@link ConnectionCustomizer#masterTimeout}
     *
     * @classdesc
     * A ConnectionCustomizer uses an established connection to a device
     * to transfer sets of configuration values over it.
     */
    constructor: function(options) {
        Customizer.apply(this, arguments);

        _.extend(this, _.pick(options, optionKeys));
    },

    loadConfiguration: function() {
        var _this = this;

        var options = {
            action: 'get',
        };

        var callback = function(config, round) {
            if (round === 1) {
                return _this._getInitialConfiguration(config);
            } else {
                return _this._optimizeConfiguration(config);
            }
        };

        return this.transceiveConfiguration(options, callback);
    },

    saveConfiguration: function(configuration) {
        var _this = this;

        var options = {
            action: 'set',
            actionOptions: {
                save: true,
            },
        };

        var callback = function(config, round) {
            if (round === 1) {
                return _this._getInitialConfiguration(config);
            } else {
                return _this._optimizeConfiguration(config);
            }
        };

        return this.transceiveConfiguration(options, callback);
    },

    /**
     * Transceives a controller configuration set, handling timeouts, retries etc.
     *
     * @param {object} options Options
     * @param {number} [options.maxRounds] {@link ConnectionCustomizer#maxRounds}
     * @param {number} [options.triesPerValue] {@link ConnectionCustomizer#triesPerValue}
     * @param {number} [options.timeoutPerValue] {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} [options.masterTimeout] {@link ConnectionCustomizer#masterTimeout}
     * @param {number} options.action Action to perform, can be `'get'` or `'set'`.
     * @param {number} [options.actionOptions] Options object to forward to the action to perform.
     * @return {object} Promise that resolves to the configuration or `null` on timeout.
     */
    transceiveConfiguration: function(options, callback) {
        var _this = this;

        if (_.isFunction(options)) {
            callback = options;
            options = null;
        }

        options = _.defaults({}, options, {
            maxRounds: this.maxRounds,
            triesPerValue: this.triesPerValue,
            timeoutPerValue: this.timeoutPerValue,
            masterTimeout: this.masterTimeout,
            action: null,
            actionOptions: null,
        });

        var connection = this.connection;
        var address = this.deviceAddress;

        return utils.cancelablePromise(function(resolve, reject, notify, checkCanceled) {
            var config = null;

            var state = {
                masterAddress: null,
                masterLastContacted: null,
            };

            var round = 0;

            var reportProgress = function(progress) {
                if (_.isString(progress)) {
                    progress = {
                        message: progress,
                    };
                }

                _.extend(progress, {
                    round: round,
                });

                notify(progress);
            };

            var nextRound = function() {
                if (round < options.maxRounds) {
                    round++;

                    Q.fcall(checkCanceled).then(function() {
                        reportProgress('OPTIMIZING_VALUES');

                        return callback(config, round);
                    }).then(checkCanceled).then(function(newConfig) {
                        config = newConfig;

                        var pendingValues = _.filter(config, function(value) {
                            return value.pending;
                        });

                        var index = 0;

                        var nextValue = function() {
                            if (index < pendingValues.length) {
                                var valueInfo = pendingValues [index++];

                                Q.fcall(checkCanceled).then(function() {
                                    return _this.transceiveValue(valueInfo.valueIndex, valueInfo.value, {
                                        triesPerValue: options.triesPerValue,
                                        timeoutPerValue: options.timeoutPerValue,
                                        action: options.action,
                                        actionOptions: options.actionOptions,
                                    }, state).progress(function(progress) {
                                        reportProgress(progress);
                                    });
                                }).then(function(datagram) {
                                    valueInfo.pending = false;
                                    valueInfo.transceived = !!datagram;

                                    if (datagram) {
                                        valueInfo.value = datagram.value;
                                    }

                                    nextValue();
                                }).done();
                            } else {
                                nextRound(config);
                            }
                        };

                        if (pendingValues.length > 0) {
                            process.nextTick(nextValue);
                        } else {
                            Q.fcall(function() {
                                if (state.masterLastContacted !== null) {
                                    reportProgress('RELEASING_BUS');

                                    return connection.releaseBus(address);
                                }
                            }).then(function() {
                                resolve(config);
                            }).done();
                        }
                    }).fail(reject).done();
                } else {
                    resolve(null);
                }
            };

            process.nextTick(nextRound);
        });
    },

    /**
     * Transceive a controller value over this connection, handling
     * timeouts, retries etc.
     *
     * @param {number} valueIndex Value index
     * @param {number} value Value
     * @param {object} options Options
     * @param {number} options.triesPerValue {@link ConnectionCustomizer#triesPerValue}
     * @param {number} options.timeoutPerValue {@link ConnectionCustomizer#timeoutPerValue}
     * @param {number} options.masterTimeout {@link ConnectionCustomizer#masterTimeout}
     * @param {object} state State to share between multiple calls to this method.
     * @returns {object} Promise that resolves with the datagram received or `null` on timeout.
     */
    transceiveValue: function(valueIndex, value, options, state) {
        if (state === undefined) {
            state = {};
        }

        options = _.defaults({}, options, {
            triesPerValue: this.triesPerValue,
            timeoutPerValue: this.timeoutPerValue,
            masterTimeout: this.masterTimeout,
            action: null,
            actionOptions: null,
        });

        state = _.defaults(state, {
            masterAddress: this.deviceAddress,
            masterLastContacted: Date.now(),
        });

        var connection = this.connection;
        var address = this.deviceAddress;

        return utils.cancelablePromise(function(resolve, reject, notify, checkCanceled) {
            var timer, onConnectionState;

            var done = function(err, result) {
                if (timer) {
                    clearTimeout(timer);
                    timer = null;
                }

                if (onConnectionState) {
                    connection.removeListener('connectionState', onConnectionState);
                }

                if (err) {
                    reject(err);
                } else {
                    resolve(result);
                }
            };

            var check = function(result) {
                return Q.fcall(function() {
                    return checkCanceled();
                }).then(function() {
                    return connection.createConnectedPromise();
                });
            };

            var tries = 0;

            var reportProgress = function(message) {
                notify({
                    message: message,
                    tries: tries,
                });
            };

            var nextTry = function() {
                if (tries < options.triesPerValue) {
                    tries++;

                    Q.fcall(check).then(function() {
                        if ((tries > 1) && (state.masterLastContacted !== null)) {
                            reportProgress('RELEASING_BUS');

                            state.masterLastContacted = null;

                            return connection.releaseBus(state.masterAddress);
                        }
                    }).then(check).then(function() {
                        if (state.masterLastContacted === null) {
                            reportProgress('WAITING_FOR_FREE_BUS');

                            return connection.waitForFreeBus().then(function(datagram) { // TODO: optional timeout?
                                if (datagram) {
                                    state.masterAddress = datagram.sourceAddress;
                                } else {
                                    state.masterAddress = null;
                                }
                            });
                        }
                    }).then(check).then(function() {
                        var contactMaster;
                        if (state.masterAddress === null) {
                            contactMaster = false;
                        } else if (state.masterAddress === address) {
                            contactMaster = false;
                        } else if (state.masterLastContacted === null) {
                            contactMaster = true;
                        } else if ((Date.now() - state.masterLastContacted) >= options.masterTimeout) {
                            contactMaster = true;
                        } else {
                            contactMaster = false;
                        }
                        if (contactMaster) {
                            reportProgress('CONTACTING_MASTER');

                            state.masterLastContacted = Date.now();

                            return connection.getValueById(state.masterAddress, 0, {
                                timeout: 500,
                                tries: 1,
                            });
                        }
                    }).then(check).then(function() {
                        if (state.masterAddress === address) {
                            state.masterLastContacted = Date.now();
                        }

                        if (options.action === 'get') {
                            reportProgress('GETTING_VALUE');

                            return connection.getValueById(address, valueIndex, options.actionOptions);
                        } else if (options.action === 'set') {
                            reportProgress('SETTING_VALUE');

                            return connection.setValueById(address, valueIndex, value, options.actionOptions);
                        } else {
                            throw new Error('Unknown action "' + options.action + '"');
                        }
                    }).then(function(datagram) {
                        if (datagram) {
                            done(null, datagram);
                        } else {
                            return nextTry();
                        }
                    }).fail(function(reason) {
                        done(reason);
                    }).done();
                } else {
                    done(null, null);
                }
            };

            var onTimeout = function() {
                done(null, null);
            };

            process.nextTick(nextTry);
            timer = setTimeout(onTimeout, options.timeoutPerValue);
        });
    }
});

module.exports = ConnectionCustomizer;
<|endoftext|>


<commit_before>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.subtitle.EditableCaption');

/**
 * Don't call this constructor directly. Instead call the factory method in
 * unisubs.subtitle.EditableCaptionSet.
 *
 * @constructor
 * @param {Number=} opt_subOrder Order in which this sub appears. Provide
 *    this parameter iff the caption doesn't exist in the unisubs
 *    system.
 * @param {JSONCaption=} opt_jsonCaption optional JSON caption on which
 *     we're operating. Provide this parameter iff the caption exists
 *     already in the unisubs system.
 */
unisubs.subtitle.EditableCaption = function(node, x) {
    goog.events.EventTarget.call(this);
    this.previousCaption_ = null;
    this.nextCaption_ = null;

    this.node = node;
    this.x = x;

};

goog.inherits(unisubs.subtitle.EditableCaption, goog.events.EventTarget);

unisubs.subtitle.EditableCaption.prototype.fork = function(otherSub) {
    // FIXME:
    this.setStartTime(otherSub.getStarTime());
    this.setEndTime(otherSub.getEndTime());
    this.setText(otherSub.getText());
}
unisubs.subtitle.EditableCaption.orderCompare = function(a, b) {
    return a.getSubOrder() - b.getSubOrder();
};

/*
 * @const
 * @type {int}
 */
unisubs.subtitle.EditableCaption.TIME_UNDEFINED = -1;
unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER = (100 * 60 * 60) -1;

unisubs.subtitle.EditableCaption.isTimeUndefined = function(v){
    return !goog.isDefAndNotNull(v) ||
        v == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        v == unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER || false;
};

unisubs.subtitle.EditableCaption.CHANGE = 'captionchanged';

/**
 * Minimum subtitle length, in seconds.
 */
unisubs.subtitle.EditableCaption.MIN_LENGTH = 0.5;

/**
 * @param {unisubs.subtitle.EditableCaption} caption Previous caption in list.
 *
 */
unisubs.subtitle.EditableCaption.prototype.setPreviousCaption = function(caption) {
    this.previousCaption_ = caption;
};
unisubs.subtitle.EditableCaption.prototype.getPreviousCaption = function() {
    return this.previousCaption_;
};

/**
 * @param {unisubs.subtitle.EditableCaption} caption Next caption in list.
 *
 */
unisubs.subtitle.EditableCaption.prototype.setNextCaption = function(caption) {
    this.nextCaption_ = caption;
};
unisubs.subtitle.EditableCaption.prototype.getNextCaption = function() {
    return this.nextCaption_;
};
unisubs.subtitle.EditableCaption.prototype.identicalTo = function(other) {
    return this.getSubOrder() == other.getSubOrder() &&
        this.getTrimmedText() == other.getTrimmedText() &&
        this.getStartTime() == other.getStartTime() &&
        this.getEndTime() == other.getEndTime() &&
        this.getCaptionIndex() == other.getCaptionIndex() &&
        this.getStartOfParagraph() == other.getStartOfParagraph();
};
unisubs.subtitle.EditableCaption.prototype.getSubOrder = function() {
    return this.x['getSubtitleIndex'](this.node);
};
unisubs.subtitle.EditableCaption.prototype.setText = function(text, opt_dontTrack) {
    this.x['content'](this.node, text);
    this.changed_(false, opt_dontTrack);
};
unisubs.subtitle.EditableCaption.prototype.getOriginalText = function() {
    return this.json['original_text'];
};
unisubs.subtitle.EditableCaption.prototype.getHTML = function() {
    var rawText = this.x['content'](this.node);
    return this.x['markdownToHTML'](rawText);
};
unisubs.subtitle.EditableCaption.prototype.getText = function() {
    return this.x['content'](this.node);
};
unisubs.subtitle.EditableCaption.prototype.getStartOfParagraph = function(){
    return Boolean(this.x['startOfParagraph'](this.node));
};
unisubs.subtitle.EditableCaption.prototype.setStartOfParagraph = function(val, opt_dontTrack){
    if (this.getStartOfParagraph() != Boolean(val)){
        this.x['startOfParagraph'](this.node, Boolean(val));
        this.changed_(false, opt_dontTrack);
    }
    return this.getStartOfParagraph();
};
unisubs.subtitle.EditableCaption.prototype.toggleStartOfParagraph = function(){
    return this.setStartOfParagraph(!this.getStartOfParagraph(), true);
};
unisubs.subtitle.EditableCaption.prototype.getTrimmedText = function() {
    return goog.string.trim(this.x['content'](this.node));
};
unisubs.subtitle.EditableCaption.prototype.setStartTime = function(startTime) {
    var previousStartTime = this.getStartTime();
    this.setStartTime_(startTime);
    this.changed_(previousStartTime ==
                  unisubs.subtitle.EditableCaption.TIME_UNDEFINED);
};
unisubs.subtitle.EditableCaption.prototype.setStartTime_ = function(startTime) {
    var previousValue = this.x['startTime'](this.node);
    startTime = Math.max(startTime, this.getMinStartTime());
    this.x['startTime'](this.node, startTime);
    if (this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.getEndTime() < startTime +
        unisubs.subtitle.EditableCaption.MIN_LENGTH)
        this.setEndTime_(
            startTime + unisubs.subtitle.EditableCaption.MIN_LENGTH);
    if (this.previousCaption_ &&
        (this.previousCaption_.getEndTime() ==
         unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
         this.previousCaption_.getEndTime() > startTime)){
         this.previousCaption_.setEndTime(startTime);
    }
};
unisubs.subtitle.EditableCaption.prototype.getStartTime = function() {
    if (goog.isDefAndNotNull(this.x['startTime'](this.node))) {
        return this.x['startTime'](this.node);
    }
    else {
        return unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
    }
};
unisubs.subtitle.EditableCaption.prototype.setEndTime = function(endTime) {
    this.setEndTime_(endTime);
    this.changed_(false);
};
unisubs.subtitle.EditableCaption.prototype.setEndTime_ = function(endTime) {
    this.x['endTime'](this.node, endTime);
    if (this.getStartTime() > endTime -
        unisubs.subtitle.EditableCaption.MIN_LENGTH)
        this.setStartTime_(
            endTime - unisubs.subtitle.EditableCaption.MIN_LENGTH);
    if (this.nextCaption_ &&
        this.nextCaption_.getStartTime() !=
        unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.nextCaption_.getStartTime() < endTime)
        this.nextCaption_.setStartTime(endTime);
};

/**
 * Clears times. Does not issue a CHANGE event.
 */
unisubs.subtitle.EditableCaption.prototype.clearTimes = function() {
    if (this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED) {
        this.x['startTime'](this.node, '');
        this.x['endTime'](this.node, '');
    }
};

unisubs.subtitle.EditableCaption.prototype.resetSub = function() {

    this.x['content'](this.node, this.x['originalContent'](this.node));

    if (this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED) {

        var previousStartTime = this.x['startTime'](this.node);
        this.setStartTime_(this.x['originalStartTime'](this.node));
        this.changed_(previousStartTime == unisubs.subtitle.EditableCaption.TIME_UNDEFINED);

        var previousEndTime = this.x['endTime'](this.node);
        this.setEndTime_(this.x['originalEndTime'](this.node));
        this.changed_(previousEndTime == unisubs.subtitle.EditableCaption.TIME_UNDEFINED);
    }
};
unisubs.subtitle.EditableCaption.prototype.getEndTime = function() {
    if (goog.isDefAndNotNull(this.x['endTime'](this.node))) {
        return this.x['endTime'](this.node);
    }
    else {
        return unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
    }
};
unisubs.subtitle.EditableCaption.prototype.getMinStartTime = function() {
    return this.previousCaption_ ?
        (this.previousCaption_.getStartTime() +
         unisubs.subtitle.EditableCaption.MIN_LENGTH) : 0;
};
unisubs.subtitle.EditableCaption.prototype.getMaxStartTime = function() {
    if (this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED)
        return 99999;
    else
        return this.getEndTime() -
            unisubs.subtitle.EditableCaption.MIN_LENGTH;
};
unisubs.subtitle.EditableCaption.prototype.getMinEndTime = function() {
    return this.getStartTime() + unisubs.subtitle.EditableCaption.MIN_LENGTH;
};
unisubs.subtitle.EditableCaption.prototype.getMaxEndTime = function() {
    return this.nextCaption_ && this.nextCaption_.getEndTime() !=
        unisubs.subtitle.EditableCaption.TIME_UNDEFINED ?
        (this.nextCaption_.getEndTime() -
         unisubs.subtitle.EditableCaption.MIN_LENGTH) : 99999;
};
unisubs.subtitle.EditableCaption.prototype.getCaptionIndex = function() {
    return this.x['getSubtitles']().index(this.node);
};
unisubs.subtitle.EditableCaption.prototype.isShownAt = function(time) {
    return this.getStartTime() <= time &&
        (this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
            time < this.getEndTime());
};
unisubs.subtitle.EditableCaption.prototype.hasStartTimeOnly = function() {
    return this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
};

/*
 * @return {boolean} True if either startTime or endTime is not defined.
 */
unisubs.subtitle.EditableCaption.prototype.needsSync = function() {
    return this.getStartTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
};

unisubs.subtitle.EditableCaption.prototype.changed_ = function(timesFirstAssigned, opt_dontTrack) {
    if (!opt_dontTrack)
        unisubs.SubTracker.getInstance().trackEdit(this.getCaptionIndex());
    this.dispatchEvent(
        new unisubs.subtitle.EditableCaption.ChangeEvent(
            timesFirstAssigned));
};
unisubs.subtitle.EditableCaption.adjustUndefinedTiming = function(json) {
    if (!json['start_time'] || json['start_time'] == unisubs.subtitle.EditableCaption.TIME_UNDEFINED){
        json['start_time'] = unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER;
    }
    if (!json['end_time'] || json['end_time'] == unisubs.subtitle.EditableCaption.TIME_UNDEFINED){
        json['end_time'] = unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER;
    }
    return json;
};
unisubs.subtitle.EditableCaption.toJsonArray = function(editableCaptions) {
    return goog.array.map(
        editableCaptions,
        function(editableCaption) {
            return unisubs.subtitle.EditableCaption.adjustUndefinedTiming(editableCaption.json);
        });
};
unisubs.subtitle.EditableCaption.toIDArray = function(editableCaptions) {
    return goog.array.map(
        editableCaptions,
        function(ec) {
            return ec.getCaptionIndex();
        });
};

unisubs.subtitle.EditableCaption.prototype.toString = function() {
    return "EditableCaption: "  + this.getText()
}
/**
 * @constructor
 */
unisubs.subtitle.EditableCaption.ChangeEvent = function(timesFirstAssigned) {
    this.type = unisubs.subtitle.EditableCaption.CHANGE;
    this.timesFirstAssigned = timesFirstAssigned;
};
<commit_msg>Small optimization on dialog, no need to call the wrapper twice in the same function call<commit_after>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.subtitle.EditableCaption');

/**
 * Don't call this constructor directly. Instead call the factory method in
 * unisubs.subtitle.EditableCaptionSet.
 *
 * @constructor
 * @param {Number=} opt_subOrder Order in which this sub appears. Provide
 *    this parameter iff the caption doesn't exist in the unisubs
 *    system.
 * @param {JSONCaption=} opt_jsonCaption optional JSON caption on which
 *     we're operating. Provide this parameter iff the caption exists
 *     already in the unisubs system.
 */
unisubs.subtitle.EditableCaption = function(node, x) {
    goog.events.EventTarget.call(this);
    this.previousCaption_ = null;
    this.nextCaption_ = null;

    this.node = node;
    this.x = x;

};

goog.inherits(unisubs.subtitle.EditableCaption, goog.events.EventTarget);

unisubs.subtitle.EditableCaption.prototype.fork = function(otherSub) {
    // FIXME:
    this.setStartTime(otherSub.getStarTime());
    this.setEndTime(otherSub.getEndTime());
    this.setText(otherSub.getText());
}
unisubs.subtitle.EditableCaption.orderCompare = function(a, b) {
    return a.getSubOrder() - b.getSubOrder();
};

/*
 * @const
 * @type {int}
 */
unisubs.subtitle.EditableCaption.TIME_UNDEFINED = -1;
unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER = (100 * 60 * 60) -1;

unisubs.subtitle.EditableCaption.isTimeUndefined = function(v){
    return !goog.isDefAndNotNull(v) ||
        v == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        v == unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER || false;
};

unisubs.subtitle.EditableCaption.CHANGE = 'captionchanged';

/**
 * Minimum subtitle length, in seconds.
 */
unisubs.subtitle.EditableCaption.MIN_LENGTH = 0.5;

/**
 * @param {unisubs.subtitle.EditableCaption} caption Previous caption in list.
 *
 */
unisubs.subtitle.EditableCaption.prototype.setPreviousCaption = function(caption) {
    this.previousCaption_ = caption;
};
unisubs.subtitle.EditableCaption.prototype.getPreviousCaption = function() {
    return this.previousCaption_;
};

/**
 * @param {unisubs.subtitle.EditableCaption} caption Next caption in list.
 *
 */
unisubs.subtitle.EditableCaption.prototype.setNextCaption = function(caption) {
    this.nextCaption_ = caption;
};
unisubs.subtitle.EditableCaption.prototype.getNextCaption = function() {
    return this.nextCaption_;
};
unisubs.subtitle.EditableCaption.prototype.identicalTo = function(other) {
    return this.getSubOrder() == other.getSubOrder() &&
        this.getTrimmedText() == other.getTrimmedText() &&
        this.getStartTime() == other.getStartTime() &&
        this.getEndTime() == other.getEndTime() &&
        this.getCaptionIndex() == other.getCaptionIndex() &&
        this.getStartOfParagraph() == other.getStartOfParagraph();
};
unisubs.subtitle.EditableCaption.prototype.getSubOrder = function() {
    return this.x['getSubtitleIndex'](this.node);
};
unisubs.subtitle.EditableCaption.prototype.setText = function(text, opt_dontTrack) {
    this.x['content'](this.node, text);
    this.changed_(false, opt_dontTrack);
};
unisubs.subtitle.EditableCaption.prototype.getOriginalText = function() {
    return this.json['original_text'];
};
unisubs.subtitle.EditableCaption.prototype.getHTML = function() {
    var rawText = this.x['content'](this.node);
    return this.x['markdownToHTML'](rawText);
};
unisubs.subtitle.EditableCaption.prototype.getText = function() {
    return this.x['content'](this.node);
};
unisubs.subtitle.EditableCaption.prototype.getStartOfParagraph = function(){
    return Boolean(this.x['startOfParagraph'](this.node));
};
unisubs.subtitle.EditableCaption.prototype.setStartOfParagraph = function(val, opt_dontTrack){
    if (this.getStartOfParagraph() != Boolean(val)){
        this.x['startOfParagraph'](this.node, Boolean(val));
        this.changed_(false, opt_dontTrack);
    }
    return this.getStartOfParagraph();
};
unisubs.subtitle.EditableCaption.prototype.toggleStartOfParagraph = function(){
    return this.setStartOfParagraph(!this.getStartOfParagraph(), true);
};
unisubs.subtitle.EditableCaption.prototype.getTrimmedText = function() {
    return goog.string.trim(this.x['content'](this.node));
};
unisubs.subtitle.EditableCaption.prototype.setStartTime = function(startTime) {
    var previousStartTime = this.getStartTime();
    this.setStartTime_(startTime);
    this.changed_(previousStartTime ==
                  unisubs.subtitle.EditableCaption.TIME_UNDEFINED);
};
unisubs.subtitle.EditableCaption.prototype.setStartTime_ = function(startTime) {
    var previousValue = this.x['startTime'](this.node);
    startTime = Math.max(startTime, this.getMinStartTime());
    this.x['startTime'](this.node, startTime);
    if (this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.getEndTime() < startTime +
        unisubs.subtitle.EditableCaption.MIN_LENGTH)
        this.setEndTime_(
            startTime + unisubs.subtitle.EditableCaption.MIN_LENGTH);
    if (this.previousCaption_ &&
        (this.previousCaption_.getEndTime() ==
         unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
         this.previousCaption_.getEndTime() > startTime)){
         this.previousCaption_.setEndTime(startTime);
    }
};
unisubs.subtitle.EditableCaption.prototype.getStartTime = function() {
    var val = this.x['startTime'](this.node);
    if (goog.isDefAndNotNull(val)) {
        return val;
    }
    else {
        return unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
    }
};
unisubs.subtitle.EditableCaption.prototype.setEndTime = function(endTime) {
    this.setEndTime_(endTime);
    this.changed_(false);
};
unisubs.subtitle.EditableCaption.prototype.setEndTime_ = function(endTime) {
    this.x['endTime'](this.node, endTime);
    if (this.getStartTime() > endTime -
        unisubs.subtitle.EditableCaption.MIN_LENGTH)
        this.setStartTime_(
            endTime - unisubs.subtitle.EditableCaption.MIN_LENGTH);
    if (this.nextCaption_ &&
        this.nextCaption_.getStartTime() !=
        unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.nextCaption_.getStartTime() < endTime)
        this.nextCaption_.setStartTime(endTime);
};

/**
 * Clears times. Does not issue a CHANGE event.
 */
unisubs.subtitle.EditableCaption.prototype.clearTimes = function() {
    if (this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED) {
        this.x['startTime'](this.node, '');
        this.x['endTime'](this.node, '');
    }
};

unisubs.subtitle.EditableCaption.prototype.resetSub = function() {

    this.x['content'](this.node, this.x['originalContent'](this.node));

    if (this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED) {

        var previousStartTime = this.x['startTime'](this.node);
        this.setStartTime_(this.x['originalStartTime'](this.node));
        this.changed_(previousStartTime == unisubs.subtitle.EditableCaption.TIME_UNDEFINED);

        var previousEndTime = this.x['endTime'](this.node);
        this.setEndTime_(this.x['originalEndTime'](this.node));
        this.changed_(previousEndTime == unisubs.subtitle.EditableCaption.TIME_UNDEFINED);
    }
};
unisubs.subtitle.EditableCaption.prototype.getEndTime = function() {
    var val = this.x['endTime'](this.node);
    if (goog.isDefAndNotNull(val)) {
        return val;
    }
    else {
        return unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
    }
};
unisubs.subtitle.EditableCaption.prototype.getMinStartTime = function() {
    return this.previousCaption_ ?
        (this.previousCaption_.getStartTime() +
         unisubs.subtitle.EditableCaption.MIN_LENGTH) : 0;
};
unisubs.subtitle.EditableCaption.prototype.getMaxStartTime = function() {
    if (this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED)
        return 99999;
    else
        return this.getEndTime() -
            unisubs.subtitle.EditableCaption.MIN_LENGTH;
};
unisubs.subtitle.EditableCaption.prototype.getMinEndTime = function() {
    return this.getStartTime() + unisubs.subtitle.EditableCaption.MIN_LENGTH;
};
unisubs.subtitle.EditableCaption.prototype.getMaxEndTime = function() {
    return this.nextCaption_ && this.nextCaption_.getEndTime() !=
        unisubs.subtitle.EditableCaption.TIME_UNDEFINED ?
        (this.nextCaption_.getEndTime() -
         unisubs.subtitle.EditableCaption.MIN_LENGTH) : 99999;
};
unisubs.subtitle.EditableCaption.prototype.getCaptionIndex = function() {
    return this.x['getSubtitles']().index(this.node);
};
unisubs.subtitle.EditableCaption.prototype.isShownAt = function(time) {
    return this.getStartTime() <= time &&
        (this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
            time < this.getEndTime());
};
unisubs.subtitle.EditableCaption.prototype.hasStartTimeOnly = function() {
    return this.getStartTime() != unisubs.subtitle.EditableCaption.TIME_UNDEFINED &&
        this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
};

/*
 * @return {boolean} True if either startTime or endTime is not defined.
 */
unisubs.subtitle.EditableCaption.prototype.needsSync = function() {
    return this.getStartTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED ||
        this.getEndTime() == unisubs.subtitle.EditableCaption.TIME_UNDEFINED;
};

unisubs.subtitle.EditableCaption.prototype.changed_ = function(timesFirstAssigned, opt_dontTrack) {
    if (!opt_dontTrack)
        unisubs.SubTracker.getInstance().trackEdit(this.getCaptionIndex());
    this.dispatchEvent(
        new unisubs.subtitle.EditableCaption.ChangeEvent(
            timesFirstAssigned));
};
unisubs.subtitle.EditableCaption.adjustUndefinedTiming = function(json) {
    if (!json['start_time'] || json['start_time'] == unisubs.subtitle.EditableCaption.TIME_UNDEFINED){
        json['start_time'] = unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER;
    }
    if (!json['end_time'] || json['end_time'] == unisubs.subtitle.EditableCaption.TIME_UNDEFINED){
        json['end_time'] = unisubs.subtitle.EditableCaption.TIME_UNDEFINED_SERVER;
    }
    return json;
};
unisubs.subtitle.EditableCaption.toJsonArray = function(editableCaptions) {
    return goog.array.map(
        editableCaptions,
        function(editableCaption) {
            return unisubs.subtitle.EditableCaption.adjustUndefinedTiming(editableCaption.json);
        });
};
unisubs.subtitle.EditableCaption.toIDArray = function(editableCaptions) {
    return goog.array.map(
        editableCaptions,
        function(ec) {
            return ec.getCaptionIndex();
        });
};

unisubs.subtitle.EditableCaption.prototype.toString = function() {
    return "EditableCaption: "  + this.getText()
}
/**
 * @constructor
 */
unisubs.subtitle.EditableCaption.ChangeEvent = function(timesFirstAssigned) {
    this.type = unisubs.subtitle.EditableCaption.CHANGE;
    this.timesFirstAssigned = timesFirstAssigned;
};
<|endoftext|>


<commit_before>var db = require( '../db.js' );
var logger = require( '../logger.js' ).logger;
var syslogger = require('../logger.js').sysLogger;
var timer = require('../timer.js');
var candidate = require('../models/candidate.js').baseCandidate;

class duelCandidate extends candidate{
    constructor(id, name, firstRoundVote){
        super(id, name);
		this.vote = 0;
		this.score = 0;
		this.firstRoundVote = firstRoundVote;
    }

    addVote(count){
        this.vote += count;
    }

    setVote(count){
        this.vote = count;
	}

	setScore(count){
		this.score = count;
	}

	getTotal(){
		//temp function, might have to change in the future
		return this.vote * 0.45 + this.score * 0.45 + this.firstRoundVote * 0.1;
	}
}

class duelMatch{
	constructor(){
		this.state = 'IDLE';
		this.initialized = false;
		this.roundNumber = 0;
        this.timer = new timer(120000, ()=>{this.state = 'VOTED'}, 1000);
	}

	init(listOfCandidates){
		this.initialized = false;
		return db.runQuery('SELECT * FROM smsvoting.candidates where c_id in ( ' + listOfCandidates + ' );')
		.then((res) =>
			db.runQuery('SELECT * FROM smsvoting.group_result where id in ( ' + listOfCandidates + ' );').then((res2) => {
				let firstRoundCandidate = res2.find((y) => y['id'] === x['c_id']);
				let firstRoundVote = firstRoundCandidate ? firstRoundCandidate['votes'] : 0;
				this.listOfCandidates = res.map((x) => new duelCandidate(x['c_id'], x['c_name'], firstRoundVote));
				syslogger.info('Duel match initialized, candidates - ' + listOfCandidates);
				this.initialized = true;
			}))
		.catch((err) => syslogger.error('Cannot initialize match ' + err));
	}

	setState(newstate){
		this.state = newstate;
        if (newstate === 'VOTING'){
        	this.timer.start();
        } else {
        	this.timer.stop();
		}
        if (newstate === 'RESULT'){
        	this.writeResultToDb();
		}
	}

	setRound(roundId){
		db.runQuery('SELECT cids FROM smsvoting.duel_matches where round_id = ?', [roundId]).then((res) => {
			this.init(res[0]['cids']);
			this.roundNumber = roundId;
		});
	}

	processCommand(body){
		if (body.opcode == 'setround'){
			this.setRound(body.roundId);
		} else if(this.initialized){
			switch(body.opcode){
				case 'setstate':
					this.setState(body.newState);
					break;
				case 'addvote':
					body.reset ? this.setCandidateVote(body.candidate, parseInt(body.score)) : this.addVoteToCandidate(body.candidate, parseInt(body.score));
					break;
				case 'setscore':
					this.setCandidateScore(body.candidate, parseInt(body.score));
				default:
					syslogger.error('Invalid opcode - ' + body.opcode);
			}
		} else {
			syslogger.error('Match not initialized yet');
		}
	}

	processVote(voteString, user){
		let person = this.listOfCandidates.find((x) => {return (x.id === parseInt(voteString))});
		return new Promise((resolve, reject) => {
			if (typeof person === 'udnefined'){
				logger.error("Received invalid vote to " + voteString + " from user " + user);
				return reject();
			} else {
				db.runQuery('INSERT INTO duel_votes(round, cid, voter) VALUES( ?, ?, ? )', [this.roundNumber, voteString, user ]).then(() => {
					person.addVote(1);
					logger.info('User ' + user + ' has voted on ' + voteString);
					return resolve(voteString);
				}).catch((err) => {
					logger.error('Invalid vote to ' + voteString + ' from ' + user + ' - ' + err );
					return reject();
				});
			}
		});
    }

	writeResultToDb(){
		for(var candidate in this.listOfCandidates){
            db.runQuery('INSERT INTO smsvoting.duel_result(c_id, round_id, score, votes) VALUES( ?, ?, ?, ? )', [this.listOfCandidates[candidate].id, this.roundNumber, this.listOfCandidates[candidate].score, this.listOfCandidates[candidate].vote]);
        }
	}

	isVoting(){
        return (this.state === 'VOTING');
	}

	addVoteToCandidate(id, votecount){
        this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).addVote(votecount);
    }

    setCandidateVote(id, votecount){
        this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).setVote(votecount);
	}

	setCandidateScore(id, scorecount){
		this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).setScore(scorecount);
	}

	compileResult(){
        return {state: this.state, type: 'Duel', round: this.roundNumber, timerRemain: this.timer.getRemaining(), data: (this.state === 'IDLE') ? null : this.listOfCandidates};
	}

	getMatchType(){
		return 'Duel';
	}
}

module.exports.duelMatch = duelMatch;<commit_msg>Fixed duel init<commit_after>var db = require( '../db.js' );
var logger = require( '../logger.js' ).logger;
var syslogger = require('../logger.js').sysLogger;
var timer = require('../timer.js');
var candidate = require('../models/candidate.js').baseCandidate;

class duelCandidate extends candidate{
    constructor(id, name, firstRoundVote){
        super(id, name);
		this.vote = 0;
		this.score = 0;
		this.firstRoundVote = firstRoundVote;
    }

    addVote(count){
        this.vote += count;
    }

    setVote(count){
        this.vote = count;
	}

	setScore(count){
		this.score = count;
	}

	getTotal(){
		//temp function, might have to change in the future
		return this.vote * 0.45 + this.score * 0.45 + this.firstRoundVote * 0.1;
	}
}

class duelMatch{
	constructor(){
		this.state = 'IDLE';
		this.initialized = false;
		this.roundNumber = 0;
        this.timer = new timer(120000, ()=>{this.state = 'VOTED'}, 1000);
	}

	init(listOfCandidates){
		this.initialized = false;
		return db.runQuery('SELECT * FROM smsvoting.candidates where c_id in ( ' + listOfCandidates + ' );')
		.then((res) =>
			db.runQuery('SELECT * FROM smsvoting.group_result where id in ( ' + listOfCandidates + ' );').then((res2) => {
				this.listOfCandidates = res.map((x) => {
					let firstRoundCandidate = res2.find((y) => y['id'] === x['c_id']);
					let firstRoundVote = firstRoundCandidate ? firstRoundCandidate['votes'] : 0;
					return new duelCandidate(x['c_id'], x['c_name'], firstRoundVote);
				})
				syslogger.info('Duel match initialized, candidates - ' + listOfCandidates);
				this.initialized = true;
			}))
		.catch((err) => syslogger.error('Cannot initialize match ' + err));
	}

	setState(newstate){
		this.state = newstate;
        if (newstate === 'VOTING'){
        	this.timer.start();
        } else {
        	this.timer.stop();
		}
        if (newstate === 'RESULT'){
        	this.writeResultToDb();
		}
	}

	setRound(roundId){
		db.runQuery('SELECT cids FROM smsvoting.duel_matches where round_id = ?', [roundId]).then((res) => {
			this.init(res[0]['cids']);
			this.roundNumber = roundId;
		});
	}

	processCommand(body){
		if (body.opcode == 'setround'){
			this.setRound(body.roundId);
		} else if(this.initialized){
			switch(body.opcode){
				case 'setstate':
					this.setState(body.newState);
					break;
				case 'addvote':
					body.reset ? this.setCandidateVote(body.candidate, parseInt(body.score)) : this.addVoteToCandidate(body.candidate, parseInt(body.score));
					break;
				case 'setscore':
					this.setCandidateScore(body.candidate, parseInt(body.score));
					break;
				default:
					syslogger.error('Invalid opcode - ' + body.opcode);
			}
		} else {
			syslogger.error('Match not initialized yet');
		}
	}

	processVote(voteString, user){
		let person = this.listOfCandidates.find((x) => {return (x.id === parseInt(voteString))});
		return new Promise((resolve, reject) => {
			if (typeof person === 'udnefined'){
				logger.error("Received invalid vote to " + voteString + " from user " + user);
				return reject();
			} else {
				db.runQuery('INSERT INTO duel_votes(round, cid, voter) VALUES( ?, ?, ? )', [this.roundNumber, voteString, user ]).then(() => {
					person.addVote(1);
					logger.info('User ' + user + ' has voted on ' + voteString);
					return resolve(voteString);
				}).catch((err) => {
					logger.error('Invalid vote to ' + voteString + ' from ' + user + ' - ' + err );
					return reject();
				});
			}
		});
    }

	writeResultToDb(){
		for(var candidate in this.listOfCandidates){
            db.runQuery('INSERT INTO smsvoting.duel_result(c_id, round_id, score, votes) VALUES( ?, ?, ?, ? )', [this.listOfCandidates[candidate].id, this.roundNumber, this.listOfCandidates[candidate].score, this.listOfCandidates[candidate].vote]);
        }
	}

	isVoting(){
        return (this.state === 'VOTING');
	}

	addVoteToCandidate(id, votecount){
        this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).addVote(votecount);
    }

    setCandidateVote(id, votecount){
        this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).setVote(votecount);
	}

	setCandidateScore(id, scorecount){
		this.listOfCandidates.find((x) => {return (x.id === parseInt(id))}).setScore(scorecount);
	}

	compileResult(){
        return {state: this.state, type: 'Duel', round: this.roundNumber, timerRemain: this.timer.getRemaining(), data: (this.state === 'IDLE') ? null : this.listOfCandidates};
	}

	getMatchType(){
		return 'Duel';
	}
}

module.exports.duelMatch = duelMatch;<|endoftext|>


<commit_before>import React, { Component } from 'react';

export default class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = { term: '' };
  }

  onInputChange(event) {
    console.log(event.target.value);
  }

  render() {
    return(
      <form className="input-group">
        <input
          placeholder="Give a five-day forecast in your favorite cities"
          className="form-control"
          value={this.state.term}
          onChange={this.onInputChange}
        />

          <button type="submit" className="btn btn-secondary">Search</button>

      </form>
    );
  }
}
<commit_msg>Binding context of this to the onInputChange function<commit_after>import React, { Component } from 'react';

export default class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = { term: '' };

    this.onInputChange = this.onInputChange.bind(this);
  }

  onInputChange(event) {
    console.log(event.target.value);
    this.setState({ term: event.target.value });
  }

  render() {
    return(
      <form className="input-group">
        <input
          placeholder="Give a five-day forecast in your favorite cities"
          className="form-control"
          value={this.state.term}
          onChange={this.onInputChange}
        />

          <button type="submit" className="btn btn-secondary">Search</button>

      </form>
    );
  }
}
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var cps = require('cps-api');
var BookingSchema = require('../models/bookingSchema.js');
var ProfileSchema = require('../models/profileSchema.js');
var cpsConn = new cps.Connection('tcp://cloud-eu-0.clusterpoint.com:9007', 'Database',
            process.env.CLUSTERPOINT_USERNAME, process.env.CLUSTERPOINT_PASSWORD,
            'document', 'document/id', {account: 1393});

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

router.post('/signup', function(req, res, next) {
  var newProfile = new ProfileSchema(req.body);
  var document = [newProfile];
  cpsConn.sendRequest(new cps.InsertRequest(document), function (err, resp) {
    if (err){
      res.json(err);
      return;
    }
     res.json(resp);
  });
});

router.post('/login', function(req, res, next) {
  var retrieve_req = new cps.RetrieveRequest(req.body.username);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');
});

router.get('/artists/:id', function(req, res, next){
  var retrieve_req = new cps.RetrieveRequest(req.params.id);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
        res.json(err);
        return;
      }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');;
});

router.patch('/artists/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/artists/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/venues/', function(req, res, next){
  var search_req = new cps.SearchRequest(cps.Term("venue", "userType"),
      0, 20);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       };
       console.log(search_resp.results.document);
    });
});

router.get('/venues/:id', function(req, res, next){
  var retrieve_req = new cps.RetrieveRequest(req.params.id);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
        res.json(err);
        return;
      }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');;
});

router.patch('/venues/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/venues/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/events/', function(req, res, next){
  var search_req = new cps.SearchRequest(cps.Term("event", "type"),
      0, 20);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       }
       res.json(search_resp.results.document);
    });
});

router.post('/events', function(req, res, next) {
  var newEvent = new BookingSchema(req.body);
  var document = [newProfile];
  cpsConn.sendRequest(new cps.InsertRequest(document), function (err, resp) {
    if (err){
      res.json(err);
      return;
    }
     res.json(resp);
  });
});

router.patch('/events/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/events/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/search/', function(req, res, next){
  var search_req = new cps.SearchRequest("k*", 0, 10);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       }
       res.json(search_resp.results.document);
    });
});

module.exports = router;
<commit_msg>fix login endpoint<commit_after>var express = require('express');
var router = express.Router();
var cps = require('cps-api');
var BookingSchema = require('../models/bookingSchema.js');
var ProfileSchema = require('../models/profileSchema.js');
var cpsConn = new cps.Connection('tcp://cloud-eu-0.clusterpoint.com:9007', 'Database',
            process.env.CLUSTERPOINT_USERNAME, process.env.CLUSTERPOINT_PASSWORD,
            'document', 'document/id', {account: 1393});

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

router.post('/signup', function(req, res, next) {
  var newProfile = new ProfileSchema(req.body);
  var document = [newProfile];
  cpsConn.sendRequest(new cps.InsertRequest(document), function (err, resp) {
    if (err){
      res.json(err);
      return;
    }
     res.json(resp);
  });
});

router.get('/login', function(req, res, next) {
  var retrieve_req = new cps.RetrieveRequest(req.body.id);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');
});

router.get('/artists/:id', function(req, res, next){
  var retrieve_req = new cps.RetrieveRequest(req.params.id);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
        res.json(err);
        return;
      }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');;
});

router.patch('/artists/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/artists/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/venues/', function(req, res, next){
  var search_req = new cps.SearchRequest(cps.Term("venue", "userType"),
      0, 20);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       };
       console.log(search_resp.results.document);
    });
});

router.get('/venues/:id', function(req, res, next){
  var retrieve_req = new cps.RetrieveRequest(req.params.id);
  cpsConn.sendRequest(retrieve_req, function (err, retrieve_resp) {
     if (err){
        res.json(err);
        return;
      }
     if (retrieve_resp) {
        res.json(retrieve_resp.results.document[0]);
     }
  }, 'json');;
});

router.patch('/venues/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/venues/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/events/', function(req, res, next){
  var search_req = new cps.SearchRequest(cps.Term("event", "type"),
      0, 20);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       }
       res.json(search_resp.results.document);
    });
});

router.post('/events', function(req, res, next) {
  var newEvent = new BookingSchema(req.body);
  var document = [newProfile];
  cpsConn.sendRequest(new cps.InsertRequest(document), function (err, resp) {
    if (err){
      res.json(err);
      return;
    }
     res.json(resp);
  });
});

router.patch('/events/:id', function(req, res, next){
  var replace_request = new cps.PartialReplaceRequest(req.body);
  cpsConn.sendRequest(replace_request, function (err, replace_resp) {
     if (err){
       res.json(err);
       return;
     }
     if (replace_resp) {
        res.json(replace_resp);
     }
  }, 'json');
});

router.delete('/events/:id', function(req, res, next){
  cpsConn.sendRequest(new cps.DeleteRequest({ id: req.params.id }), function (err, delete_resp) {
     if (err){
       res.json(err);
       return;
     }
     if(delete_resp){
      res.json(delete_resp);
     }
  });
});

router.get('/search/', function(req, res, next){
  var search_req = new cps.SearchRequest("k*", 0, 10);
    cpsConn.sendRequest(search_req, function (err, search_resp) {
       if (err){
         res.json(err);
         return;
       }
       res.json(search_resp.results.document);
    });
});

module.exports = router;
<|endoftext|>


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

Copyright (c) 2016, Highsoft

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

/** Slider widget
 *  @constructor
 *
 *  @emits Change - when the value changes
 *
 *  @param parent {domnode} - the parent of the slider
 *  @param attributes {object} - the slider properties
 *    > max {number} - the max value
 *    > min {number} - the min value
 *    > step {number} - the step size
 *    > resetTo {anything} - value to reset to
 */
highed.Slider = function (parent, attributes) {
    var properties = highed.merge({
            max: 100,
            min: 1,
            step: 1,
            resetTo: 0
        }, attributes),
        events = highed.events(),
        value = properties.resetTo,
        container = highed.dom.cr('div', 'highed-slider'),
        indicator = highed.dom.cr('div', 'highed-slider-indicator'),
        textIndicator = highed.dom.cr('div', 'highed-slider-text-indicator'),
        sliderBackground = highed.dom.cr('div', 'highed-slider-background'),
        resetIcon = highed.dom.cr('div', 'highed-slider-reset fa fa-undo'),
        mover = highed.Movable(indicator, 'x', true, sliderBackground)
    ;

    ////////////////////////////////////////////////////////////////////////////

    // Calculate the indicator X
    function calcIndicator() {
        var x = 0,
            s = highed.dom.size(sliderBackground),
            ms = highed.dom.size(indicator)
        ;

        if (!highed.isNum(value) || !value) {
            x = 0;
        } else {
            x = ((value - properties.min) / (properties.max - properties.min)) * (s.w - ms.w);
        }

        highed.dom.style(indicator, {
            left: x + 'px'
        });
    }

    //Waits until the slider is in the dom
    function tryUpdateIndicators() {
        textIndicator.innerHTML = value;
        if (container.parentNode) {
            calcIndicator();
        } else {
            window.setTimeout(tryUpdateIndicators, 10);
        }
    }

    /** Set the value
     *  @memberof highed.Slider
     *  @param newValue {number} - the new value
     */
    function set(newValue) {
        value = highed.clamp(properties.min, properties.max, newValue);
        textIndicator.innerHTML = value;
        calcIndicator();
    }

    mover.on('Moving', function (x) {
        var s = highed.dom.size(sliderBackground),
            ms = highed.dom.size(indicator)
        ;

        //Set the value based on the new X
        value = properties.min + Math.round(((x / (s.w - ms.w))) * (properties.max - properties.min));

        textIndicator.innerHTML = value;
        events.emit('Change', value);
    });

    mover.on('StartMove', function () {
        if (highed.onPhone()) {
            textIndicator.className = 'highed-slider-text-indicator highed-slider-text-indicator-popup';
        }
    });

    mover.on('EndMove', function () {
        if (highed.onPhone()) {
            textIndicator.className = 'highed-slider-text-indicator';
        }
    });

    ////////////////////////////////////////////////////////////////////////////

    highed.dom.on(resetIcon, 'mouseover', function (e) {
      //  highed.Tooltip(e.clientX, e.clientY, 'Reset to initial value');
    });

    highed.dom.on(resetIcon, 'click', function() {
        value = properties.resetTo;
        textIndicator.innerHTML = value;
        calcIndicator();

        if (value === 'null') {
            value = null;
        }
        if (value === 'undefined') {
            value = undefined;
        }

        events.emit('Change', value);
    });

    if (parent) {
        parent = highed.dom.get(parent);
        highed.dom.ap(parent, container);
    }

    highed.dom.ap(container,
        sliderBackground,
        resetIcon,
        highed.dom.ap(indicator,
            textIndicator
        )
    );

    tryUpdateIndicators();

    // Public interface
    return {
        on: events.on,
        set: set,
        container: container
    };
};
<commit_msg>Optimized slider on phones<commit_after>/******************************************************************************

Copyright (c) 2016, Highsoft

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

/** Slider widget
 *  @constructor
 *
 *  @emits Change - when the value changes
 *
 *  @param parent {domnode} - the parent of the slider
 *  @param attributes {object} - the slider properties
 *    > max {number} - the max value
 *    > min {number} - the min value
 *    > step {number} - the step size
 *    > resetTo {anything} - value to reset to
 */
highed.Slider = function (parent, attributes) {
    var properties = highed.merge({
            max: 100,
            min: 1,
            step: 1,
            resetTo: 0
        }, attributes),
        events = highed.events(),
        value = properties.resetTo,
        container = highed.dom.cr('div', 'highed-slider'),
        indicator = highed.dom.cr('div', 'highed-slider-indicator'),
        textIndicator = highed.dom.cr('div', 'highed-slider-text-indicator'),
        sliderBackground = highed.dom.cr('div', 'highed-slider-background'),
        resetIcon = highed.dom.cr('div', 'highed-slider-reset fa fa-undo'),
        mover = highed.Movable(indicator, 'x', true, sliderBackground)
    ;

    ////////////////////////////////////////////////////////////////////////////

    // Calculate the indicator X
    function calcIndicator() {
        var x = 0,
            s = highed.dom.size(sliderBackground),
            ms = highed.dom.size(indicator)
        ;

        if (!highed.isNum(value) || !value) {
            x = 0;
        } else {
            x = ((value - properties.min) / (properties.max - properties.min)) * (s.w - ms.w);
        }

        highed.dom.style(indicator, {
            left: x + 'px'
        });
    }

    //Waits until the slider is in the dom
    function tryUpdateIndicators() {
        textIndicator.innerHTML = value;
        if (container.parentNode) {
            calcIndicator();
        } else {
            window.setTimeout(tryUpdateIndicators, 10);
        }
    }

    /** Set the value
     *  @memberof highed.Slider
     *  @param newValue {number} - the new value
     */
    function set(newValue) {
        value = highed.clamp(properties.min, properties.max, newValue);
        textIndicator.innerHTML = value;
        calcIndicator();
    }

    mover.on('Moving', function (x) {
        var s = highed.dom.size(sliderBackground),
            ms = highed.dom.size(indicator)
        ;

        //Set the value based on the new X
        value = properties.min + Math.round(((x / (s.w - ms.w))) * (properties.max - properties.min));

        textIndicator.innerHTML = value;
        if (!highed.onPhone()) {
            events.emit('Change', value);
        }
    });

    mover.on('StartMove', function () {
        if (highed.onPhone()) {
            textIndicator.className = 'highed-slider-text-indicator highed-slider-text-indicator-popup';
        }
    });

    mover.on('EndMove', function () {
        if (highed.onPhone()) {
            textIndicator.className = 'highed-slider-text-indicator';
            //We're not emitting changes until done on mobile
            events.emit('Change', value);
        }
    });

    ////////////////////////////////////////////////////////////////////////////

    highed.dom.on(resetIcon, 'mouseover', function (e) {
      //  highed.Tooltip(e.clientX, e.clientY, 'Reset to initial value');
    });

    highed.dom.on(resetIcon, 'click', function() {
        value = properties.resetTo;
        textIndicator.innerHTML = value;
        calcIndicator();

        if (value === 'null') {
            value = null;
        }
        if (value === 'undefined') {
            value = undefined;
        }

        events.emit('Change', value);
    });

    if (parent) {
        parent = highed.dom.get(parent);
        highed.dom.ap(parent, container);
    }

    highed.dom.ap(container,
        sliderBackground,
        resetIcon,
        highed.dom.ap(indicator,
            textIndicator
        )
    );

    tryUpdateIndicators();

    // Public interface
    return {
        on: events.on,
        set: set,
        container: container
    };
};
<|endoftext|>


<commit_before>
/* Asset Minifier */

var app = protos.app,
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    config = app.asset_compiler,
    Multi = require('multi');

var cleancss = protos.requireDependency('clean-css', 'Asset Compiler', 'asset_compiler');
var uglifyjs = protos.requireDependency('uglify-js', 'Asset Compiler', 'asset_compiler');

var minifyTargets = Object.keys(config.minify);

var compiler = new Multi({
  getSource: function(f, target, callback) {
    var file = app.fullPath('public/' + f);
    var ext = getExt(file);
    var source = fs.readFileSync(file, 'utf8').toString();
    source = app.applyFilters('asset_compiler_minify_source', source, ext, f);
    source = resolvePaths(source, f, target);
    if (ext in config.compilers) {
      config.compilers[ext](source, file, callback);
    } else {
      callback(null, source);
    }
  }
});

// Recursive minification
function minification() {
  var sources, target = minifyTargets.shift();
  if (target) {
    sources = config.minify[target];
    if (!Array.isArray(sources)) sources = [sources];
    sources.forEach(function(item, i) {
      compiler.getSource(item, target);
    });
    compiler.exec(function(err, compiled) {
      if (err) throw err;
      else {
        var ext = getExt(target);
        target = app.fullPath('public/' + target);
        if (ext == 'css') {
          var source = cleancss.process(compiled.join('\n'));
          fs.writeFileSync(target, source, 'utf8');
          app.debug("Asset Compiler: Minified CSS: " + app.relPath(target));
        } else if (ext == 'js') {
          var outSrc = minifyJS(compiled.join('\n'));
          fs.writeFileSync(target, outSrc, 'utf8');
          app.debug("Asset Compiler: Minified JavaScript: " + app.relPath(target));
        } else {
          throw new Error("Asset Compiler: Extension not supported: " + target);
        }
        minification();
      }
    });
  }
}

function resolvePaths(source, file, target) {
  var root = app.fullPath('public');
  var matches = source.match(/url\([^\)]+\)/g);
  var targetPath = util.format('%s/%s', root, path.dirname(target));
  if (matches) {
    matches.forEach(function(match) {
      var data = match.replace(/(^url\(|\)$)/g, '');
      if (/^['"]*(data|http):/.test(data) === false ) { // Ignore data:uris and http://
        var p = data.replace(/['"]+/g, '');
        var filePath = path.resolve(util.format('%s/%s/%s', root, path.dirname(file), p));
        p = path.relative(targetPath, filePath);
        var replStr = util.format('url("%s")', p);
        if (replStr != match) { // Avoid infinite loop
          while (source.indexOf(match) >= 0) {
            source = source.replace(match, replStr);
          }
        }
      }
    });
  }
  return source;
}

function minifyJS(code) {
  var ast, compressor, stream;
  ast = uglifyjs.parse(code, {});
  compressor = uglifyjs.Compressor();
  ast.figure_out_scope();
  ast = ast.transform(compressor);
  ast.figure_out_scope();
  ast.compute_char_frequency();
  ast.mangle_names();
  stream = uglifyjs.OutputStream();
  ast.print(stream);
  return stream.toString();
}

function getExt(file) {
  return file.slice(file.lastIndexOf('.')+1).trim().toLowerCase();
}

// Run
minification();<commit_msg>Disable warnings when minifying js source on asset compiler<commit_after>
/* Asset Minifier */

var app = protos.app,
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    config = app.asset_compiler,
    Multi = require('multi');

var cleancss = protos.requireDependency('clean-css', 'Asset Compiler', 'asset_compiler');
var uglifyjs = protos.requireDependency('uglify-js', 'Asset Compiler', 'asset_compiler');

var minifyTargets = Object.keys(config.minify);

var compiler = new Multi({
  getSource: function(f, target, callback) {
    var file = app.fullPath('public/' + f);
    var ext = getExt(file);
    var source = fs.readFileSync(file, 'utf8').toString();
    source = app.applyFilters('asset_compiler_minify_source', source, ext, f);
    source = resolvePaths(source, f, target);
    if (ext in config.compilers) {
      config.compilers[ext](source, file, callback);
    } else {
      callback(null, source);
    }
  }
});

// Recursive minification
function minification() {
  var sources, target = minifyTargets.shift();
  if (target) {
    sources = config.minify[target];
    if (!Array.isArray(sources)) sources = [sources];
    sources.forEach(function(item, i) {
      compiler.getSource(item, target);
    });
    compiler.exec(function(err, compiled) {
      if (err) throw err;
      else {
        var ext = getExt(target);
        target = app.fullPath('public/' + target);
        if (ext == 'css') {
          var source = cleancss.process(compiled.join('\n'));
          fs.writeFileSync(target, source, 'utf8');
          app.debug("Asset Compiler: Minified CSS: " + app.relPath(target));
        } else if (ext == 'js') {
          var outSrc = minifyJS(compiled.join('\n'));
          fs.writeFileSync(target, outSrc, 'utf8');
          app.debug("Asset Compiler: Minified JavaScript: " + app.relPath(target));
        } else {
          throw new Error("Asset Compiler: Extension not supported: " + target);
        }
        minification();
      }
    });
  }
}

function resolvePaths(source, file, target) {
  var root = app.fullPath('public');
  var matches = source.match(/url\([^\)]+\)/g);
  var targetPath = util.format('%s/%s', root, path.dirname(target));
  if (matches) {
    matches.forEach(function(match) {
      var data = match.replace(/(^url\(|\)$)/g, '');
      if (/^['"]*(data|http):/.test(data) === false ) { // Ignore data:uris and http://
        var p = data.replace(/['"]+/g, '');
        var filePath = path.resolve(util.format('%s/%s/%s', root, path.dirname(file), p));
        p = path.relative(targetPath, filePath);
        var replStr = util.format('url("%s")', p);
        if (replStr != match) { // Avoid infinite loop
          while (source.indexOf(match) >= 0) {
            source = source.replace(match, replStr);
          }
        }
      }
    });
  }
  return source;
}

function minifyJS(code) {
  var ast, compressor, stream;
  ast = uglifyjs.parse(code, {});
  compressor = uglifyjs.Compressor({warnings: false});
  ast.figure_out_scope();
  ast = ast.transform(compressor);
  ast.figure_out_scope();
  ast.compute_char_frequency();
  ast.mangle_names();
  stream = uglifyjs.OutputStream();
  ast.print(stream);
  return stream.toString();
}

function getExt(file) {
  return file.slice(file.lastIndexOf('.')+1).trim().toLowerCase();
}

// Run
minification();<|endoftext|>


<commit_before>export function up(knex) {
  return knex.schema.table('comments', (table) => {
    table.integer('hide_type').defaultTo(0).notNullable();
    table.index('hide_type', 'comments_hide_type_idx', 'btree');
  });
}

export function down(knex) {
  return knex.schema.table('comments', (table) => {
    table.dropIndex('', 'comments_hide_type_idx');
    table.dropColumn('hide_type');
  });
}
<commit_msg>Allow null in comments.user_id in DB migration<commit_after>export async function up(knex) {
  await knex.schema
    .table('comments', (table) => {
      table.integer('hide_type').defaultTo(0).notNullable();
      table.index('hide_type', 'comments_hide_type_idx', 'btree');
    })
    .raw('alter table "comments" alter column "user_id" drop not null')
    .raw('alter table "comments" add constraint "comments_user_id_check" check (("hide_type" = 0) = ("user_id" is not null))');
}

export async function down(knex) {
  const { rows: [{ exists }] } = await knex.raw('select exists(select 1 from "comments" where "user_id" is null)');
  if (exists) {
    throw new Error('There are comments with null value in "user_id" column. Remove them before start this migration.');
  }

  await knex.schema
    .raw('alter table "comments" drop constraint "comments_user_id_check"')
    .raw('alter table "comments" alter column "user_id" set not null')
    .table('comments', (table) => {
      table.dropIndex('', 'comments_hide_type_idx');
      table.dropColumn('hide_type');
    });
}
<|endoftext|>