<commit_before>/**
 * Класс вычислений оставшегося времени для лотов
 * Время синхронезируется с сервером
 * @constructor
 * @dependencies {jQuery}
 * @param options {Object} - настройки
 * @param options.syncUrl {String} - URL для обновления серверного времени
 * @param options.syncLotsUrl {String} - URL для обновления данных лотов
 * @param options.durationMode {String} - настройка в значении 'ProlongationByLots' не дает загружать данне лотов
 */
function LotTimer(options) {

    this._syncUrl = options.syncUrl;
    this._syncLotsUrl = options.syncLotsUrl;
    this._durationMode = options.durationMode;
    this._intervalTimeOfSyncServerTime = options.syncInterval;
    this._lotStatusActiveText = options.lotStatusActiveText;
    this._lotStatusStopText = options.lotStatusStopText;

    this.syncServerTime();
    this.startRecursiveSyncServerTime();
    this.startTimerUpdateLotsRemaindersTime();
}

LotTimer.prototype = {

    /**
     * URL для синхронизации серверного времени
     */
    _syncUrl: '',

    /**
     * Серверное время
     * @type {timestamp}
     */
    _serverTime: null,

    /**
     * Хранилище ссылок на закрытые лоты
     */
    _disableLots: {},

    /**
     * Остаток времени
     * Обновляеться с сервера переодически - корректируеться
     * А так же обновляется при выислении по таймеру
     * @type {timestamp}
     */
    _remainderTime: null,

    /**
     * Если параметр выставлен в "ProlongationByLots" - проиходит обновление лотов
     */
    _durationMode: null,

    /**
     * Идентификатор таймера синхронизации данных серверного времени
     */
    __timerServerTime: null,

    /**
     * Таймер обновления времени всех лотов
     */
    __timerOfUpdateLotsRemainderTime: null,

    /**
     * Хэш хранения данных о лотах в формате: {lotId: lotRemainderTime};
     */
    dataOfLots: {},

    /**
     * Возаращает URL со случайным параметром, чтобы не кешировать
     * ответ от сервера, даже если он не поддерживает ETag
     * @param url {String} - URL запроса
     * @private
     */
    _getURLWithCMD: function (url) {
        return url + '?' + Date.now();
    },

    /**
     * Запустить переодическое обновление серверного времени
     */
    startRecursiveSyncServerTime: function () {
        this.__timerServerTime = setTimeout(function () {
            this.syncServerTime();
            this.startRecursiveSyncServerTime();
        }.bind(this),
            this._intervalTimeOfSyncServerTime
        );
    },

    /**
     * Остановить переодическое побновление серверного времени
     */
    stopRecursiveSyncServerTime: function () {
        clearTimeout(this.__timerServerTime);
    },

    /**
     * Синхронезация времени с сервером
     */
    syncServerTime: function () {
        var startSyncTime = this.getPresentTime();

        $.get(this._getURLWithCMD(this._syncUrl))
            .done(this._parseResponseServerTime.bind(this, startSyncTime))
            .fail(this.signalServerResponseFail.bind(this));
    },

    _parseResponseServerTime: function (startSyncTime, response) {
        var timeSpentOnRequest;

        if ($.type(response) !== 'object' || !response.endTimeSpan) {
            this.signalServerResponseFail();
            return;
        }

        // вычисляеться время потраченное на отправление и полуение запроса от сервера
        // делиться на два - это интересно почему, видимо из практики вычитано
        timeSpentOnRequest = Math.floor(this.getPresentTime() - startSyncTime);

        // Вычисляеться количество прошедших секунд по вычелсенному времени потраченному на запрос
        // и добавляеться к серверному времени
        this._remainderTime = this.addSecondsTo(response.endTimeSpan, Math.round(timeSpentOnRequest / 1000));

        if (this._isTimeOver()) {
            this.stopRecursiveSyncServerTime();
            setTimeout(this.signalTimeIsOver.bind(this), 0);
            return;
        }

        this.setServerTime(response.dateTime, timeSpentOnRequest);

        if (this._durationMode === 'ProlongationByLots' && response.lotsEndTime) {
            this.resolveLotsTime(response.lotsEndTime);
            this.startTimerUpdateLotsRemaindersTime();
        }
    },

    /**
     * Сохранить серверное время
     * @param dataTime {Date}
     * @param timeSpentOnRequest {timestamp}
     */
    setServerTime: function (dataTime, timeSpentOnRequest) {

        if (dataTime) {
            this._serverTime = new Date(
                dataTime.Year,
                dataTime.Month - 1,
                dataTime.Day,
                dataTime.Hour,
                dataTime.Minute,
                dataTime.Second,
                dataTime.Millisecond + timeSpentOnRequest
            );

            setTimeout(this.signalServerTimeUpdated.bind(this, this._serverTime), 0);
        }
    },

    /**
     * Обновление данных времени лотов
     * @param data
     */
    resolveLotsTime: function (data) {
      /*
      [{
       endTime:{Days: 0, Hours: 9, Minutes: 13, Seconds: 3},
       lotId:806652312,
       isLotProlongated:boolean
      }],

      */
        this.dataOfLots = data;
    },

    /**
     * Обновление времени для всех лотов в хеше
     * путем вычисления времени прошедшего с момента последнего обноавления с сервера,
     * до текущего момента
     */
    updateLotsRemaindersTime: function (cnt) {
        var i, lot,
          data = this.dataOfLots,
          sec = 0 - (cnt + 1),
          timeIsOver = false,
          $htmlRow = {
              firstColumn: '',
              timeColumn: ''
          };

        if (data == null) {
            return;
        }

        // last index
        i = data.length - 1;
        // Для ускорения цикла используем за ход сразу 10 обновлений
        while (i > -1) {

            if (lot = data[i]) {

                timeIsOver = true;

                if (!lot.isTimeOver) {
                    this.addSecondsTo(lot.endTime, sec);

                    timeIsOver = lot.isTimeOver = this._isTimeOver(lot.endTime);

                    if (timeIsOver) {

                        // вызов события об отключении строки
                        setTimeout(function (lot) {
                            this.signalTimeIsOverForLot(lot);
                        }.bind(this, lot), 0);

                        lot.isTimeOver = true;
                    }

                    data[i] = lot;
                }

                $htmlRow = this.updateHtmlRow(lot, $htmlRow, timeIsOver);

            } else break;
            --i;
        }

        this.dataOfLots = data;

        this.signalLotsUpdated($htmlRow);
    },

    updateHtmlRow: function (lot, $htmlRow, timeIsOver) {
        $htmlRow.firstColumn = this.getHTMLRowFirstColumn(
                lot.lotId,
                !timeIsOver ? 'i-active' : 'i-pending',
                !timeIsOver ? this._lotStatusActiveText : this._lotStatusStopText)
            + $htmlRow.firstColumn;

        $htmlRow.timeColumn = (timeIsOver ? this.getHTMLRowEndTimeColumn() : this.getHTMLRowTimeColumn(lot.endTime))
            + $htmlRow.timeColumn;

        return $htmlRow;
    },

    getHTMLRowFirstColumn: function (lotId, cssClass, text) {
        return ['
',
            '
'].join('');
    },

    getHTMLRowTimeColumn: function (remainingTime) {
        return ['
',
            getClockString(
                remainingTime.Hours,
                remainingTime.Minutes,
                remainingTime.Seconds
            ),
            '
'].join('');
    },

    getHTMLRowEndTimeColumn: function () {
        return '
00:00:00
';
    },

    /**
    * Запуск таймера локального перерасчета серверного времени
    * А так же переасчета оставшегося времени
    */
    startTimerUpdateLotsRemaindersTime: function () {
        var start = this.getPresentTime(),
            timeout = 1000;

        this.stopTimerUpdateLotsRemaindersTime();

        function updateTime() {
            var diff, cnt;

            diff = this.getPresentTime() - start - timeout;
            cnt = Math.floor(diff / timeout);

            if (this._remainderTime) {
                setTimeout(this.updateRemainingTime.bind(this, this.addSecondsTo(this._remainderTime, 0 - (cnt + 1))), 0);
                setTimeout(this.signalRemainingTimeUpdate.bind(this, this._remainderTime, this.dataOfLots), 0);
            }

            if (this._serverTime) {
                this._serverTime.setSeconds(this._serverTime.getSeconds(), 0 - (cnt + 1));
                setTimeout(this.signalServerTimeUpdated.bind(this, this._serverTime), 0);
            }

            diff = diff - timeout * cnt;
            start = this.getPresentTime() - diff;

            this.__timerOfUpdateLotsRemainderTime = setTimeout(
                updateTime.bind(this),
                (timeout - diff)
            );

            this.updateLotsRemaindersTime(cnt);
        }

        updateTime.call(this);
    },

    updateRemainingTime: function (remainingTime) {
        this._remainderTime = remainingTime;

        if (this._isTimeOver()) {
            this._remainderTime.Hours = 0;
            this._remainderTime.Minutes = 0;
            this._remainderTime.Seconds = 0;
            this.syncServerTime();
        }
    },

    stopTimerUpdateLotsRemaindersTime: function () {
        clearTimeout(this.__timerOfUpdateLotsRemainderTime);
    },

    /**
     * Проверка завершены ли торги или нет?
     * @param remainingTime {timestamp}
     * @private
     * @return {Boolean}
     */
    _isTimeOver: function (remainingTime) {
        var _remainingTime = remainingTime || this._remainderTime;

        return _remainingTime.Hours <= 0
            && _remainingTime.Minutes <= 0
            && _remainingTime.Seconds <= 0;
    },

    /**
     * Сигнал о завершении торгов
     * @override
     */
    signalTimeIsOver: function () { },

    /**
     * Сигнал о завершении торгов для конкретного лота
     * @override
     */
    signalTimeIsOverForLot: function () { },

    /**
     * Запрос к серверу для получения серверного времени обвалился или пришел неверный ответ
     * @override
     */
    signalServerResponseFail: function () { },

    /**
     * Сигнал о завершении оновления серверного времени
     * @param serverTime {Date} Серверное время
     * @override
     */
    signalServerTimeUpdated: function (serverTime) {
        //this.showServerTime();
        //this.showRemainingTime();
    },

    /**
     * Сигнал обновления остаточного времени
     * @param remainingTime {Object} - остаточное время
     * @override
     */
    signalRemainingTimeUpdate: function (remainingTime, dataLots) {},

    /**
     * Завершено обновление времени лотов
     * @param hashLotsTime
     */
    signalLotsUpdated: function (hashLotsTime) {},

    /**
     * Получить текущее время
     */
    getPresentTime: function () {
        return new Date().getTime();
    },

    /**
     * Добавление секунд к указанному времени
     * @param timeSpan {timestamp} указанное время
     * @param s {Number} сколько секунд надо добавить
     * @returns {timestamp}
     */
    addSecondsTo: function (timeSpan, s) {

        var ts = timeSpan.Hours * 3600;
        ts += timeSpan.Minutes * 60;
        ts += timeSpan.Seconds;

        ts += s;

        var h = ~~(ts / 3600);
        ts = ts % 3600;
        var m = ~~(ts / 60);
        ts = ts % 60;

        timeSpan.Hours = h;
        timeSpan.Minutes = m;
        timeSpan.Seconds = ts;
        return timeSpan;
    }
};
<commit_msg>update timer change irl for servertime<commit_after>/**
 * Класс вычислений оставшегося времени для лотов
 * Время синхронезируется с сервером
 * @constructor
 * @dependencies {jQuery}
 * @param options {Object} - настройки
 * @param options.syncUrl {String} - URL для обновления серверного времени
 * @param options.syncLotsUrl {String} - URL для обновления данных лотов
 * @param options.durationMode {String} - настройка в значении 'ProlongationByLots' не дает загружать данне лотов
 */
function LotTimer(options) {

    this._syncUrl = options.syncUrl;
    this._syncLotsUrl = options.syncLotsUrl;
    this._durationMode = options.durationMode;
    this._intervalTimeOfSyncServerTime = options.syncInterval;
    this._lotStatusActiveText = options.lotStatusActiveText;
    this._lotStatusStopText = options.lotStatusStopText;

    this.syncServerTime();
    this.startRecursiveSyncServerTime();
}

LotTimer.prototype = {

    /**
     * URL для синхронизации серверного времени
     */
    _syncUrl: '',

    /**
     * Серверное время
     * @type {timestamp}
     */
    _serverTime: null,

    /**
     * Хранилище ссылок на закрытые лоты
     */
    _disableLots: {},

    /**
     * Остаток времени
     * Обновляеться с сервера переодически - корректируеться
     * А так же обновляется при выислении по таймеру
     * @type {timestamp}
     */
    _remainderTime: null,

    /**
     * Если параметр выставлен в "ProlongationByLots" - проиходит обновление лотов
     */
    _durationMode: null,

    /**
     * Идентификатор таймера синхронизации данных серверного времени
     */
    __timerServerTime: null,

    /**
     * Таймер обновления времени всех лотов
     */
    __timerOfUpdateLotsRemainderTime: null,

    /**
     * Хэш хранения данных о лотах в формате: {lotId: lotRemainderTime};
     */
    dataOfLots: {},

    /**
     * Возаращает URL со случайным параметром, чтобы не кешировать
     * ответ от сервера, даже если он не поддерживает ETag
     * @param url {String} - URL запроса
     * @private
     */
    _getURLWithCMD: function (url) {
        return url + '?' + Date.now();
    },

    /**
     * Запустить переодическое обновление серверного времени
     */
    startRecursiveSyncServerTime: function () {
        this.__timerServerTime = setTimeout(function () {
            this.syncServerTime();
            this.startRecursiveSyncServerTime();
        }.bind(this),
            this._intervalTimeOfSyncServerTime
        );
    },

    /**
     * Остановить переодическое побновление серверного времени
     */
    stopRecursiveSyncServerTime: function () {
        clearTimeout(this.__timerServerTime);
    },

    /**
     * Синхронезация времени с сервером
     */
    syncServerTime: function () {
        var startSyncTime = this.getPresentTime();

        $.get(this._getURLWithCMD(this._syncLotsUrl))
            .done(this._parseResponseServerTime.bind(this, startSyncTime))
            .fail(this.signalServerResponseFail.bind(this));
    },

    _parseResponseServerTime: function (startSyncTime, response) {
        var timeSpentOnRequest;

        if ($.type(response) !== 'object' || !response.endTimeSpan) {
            this.signalServerResponseFail();
            return;
        }

        // вычисляеться время потраченное на отправление и полуение запроса от сервера
        // делиться на два - это интересно почему, видимо из практики вычитано
        timeSpentOnRequest = Math.floor(this.getPresentTime() - startSyncTime);

        // Вычисляеться количество прошедших секунд по вычелсенному времени потраченному на запрос
        // и добавляеться к серверному времени
        this._remainderTime = this.addSecondsTo(response.endTimeSpan, Math.round(timeSpentOnRequest / 1000));

        if (this._isTimeOver()) {
            this.stopRecursiveSyncServerTime();
            setTimeout(this.signalTimeIsOver.bind(this), 0);
            return;
        }

        this.setServerTime(response.dateTime, timeSpentOnRequest);

        if (this._durationMode === 'ProlongationByLots' && response.lotsEndTime) {
            this.resolveLotsTime(response.lotsEndTime);
            this.startTimerUpdateLotsRemaindersTime();
        }
    },

    /**
     * Сохранить серверное время
     * @param dataTime {Date}
     * @param timeSpentOnRequest {timestamp}
     */
    setServerTime: function (dataTime, timeSpentOnRequest) {

        if (dataTime) {
            this._serverTime = new Date(
                dataTime.Year,
                dataTime.Month - 1,
                dataTime.Day,
                dataTime.Hour,
                dataTime.Minute,
                dataTime.Second,
                dataTime.Millisecond + timeSpentOnRequest
            );

            setTimeout(this.signalServerTimeUpdated.bind(this, this._serverTime), 0);
        }
    },

    /**
     * Обновление данных времени лотов
     * @param data
     */
    resolveLotsTime: function (data) {
      /*
      [{
       endTime:{Days: 0, Hours: 9, Minutes: 13, Seconds: 3},
       lotId:806652312,
       isLotProlongated:boolean
      }],

      */
        this.dataOfLots = data;
    },

    /**
     * Обновление времени для всех лотов в хеше
     * путем вычисления времени прошедшего с момента последнего обноавления с сервера,
     * до текущего момента
     */
    updateLotsRemaindersTime: function (cnt) {
        var i, lot,
          data = this.dataOfLots,
          sec = 0 - (cnt + 1),
          timeIsOver = false,
          $htmlRow = {
              firstColumn: '',
              timeColumn: ''
          };

        if (data == null) {
            return;
        }

        // last index
        i = data.length - 1;
        // Для ускорения цикла используем за ход сразу 10 обновлений
        while (i > -1) {

            if (lot = data[i]) {

                timeIsOver = true;

                if (!lot.isTimeOver) {
                    this.addSecondsTo(lot.endTime, sec);

                    timeIsOver = lot.isTimeOver = this._isTimeOver(lot.endTime);

                    if (timeIsOver) {

                        // вызов события об отключении строки
                        setTimeout(function (lot) {
                            this.signalTimeIsOverForLot(lot);
                        }.bind(this, lot), 0);

                        lot.isTimeOver = true;
                    }

                    data[i] = lot;
                }

                $htmlRow = this.updateHtmlRow(lot, $htmlRow, timeIsOver);

            } else break;
            --i;
        }

        this.dataOfLots = data;

        this.signalLotsUpdated($htmlRow);
    },

    updateHtmlRow: function (lot, $htmlRow, timeIsOver) {
        $htmlRow.firstColumn = this.getHTMLRowFirstColumn(
                lot.lotId,
                !timeIsOver ? 'i-active' : 'i-pending',
                !timeIsOver ? this._lotStatusActiveText : this._lotStatusStopText)
            + $htmlRow.firstColumn;

        $htmlRow.timeColumn = (timeIsOver ? this.getHTMLRowEndTimeColumn() : this.getHTMLRowTimeColumn(lot.endTime))
            + $htmlRow.timeColumn;

        return $htmlRow;
    },

    getHTMLRowFirstColumn: function (lotId, cssClass, text) {
        return ['
',
            '
'].join('');
    },

    getHTMLRowTimeColumn: function (remainingTime) {
        return ['
',
            getClockString(
                remainingTime.Hours,
                remainingTime.Minutes,
                remainingTime.Seconds
            ),
            '
'].join('');
    },

    getHTMLRowEndTimeColumn: function () {
        return '
00:00:00
';
    },

    /**
    * Запуск таймера локального перерасчета серверного времени
    * А так же переасчета оставшегося времени
    */
    startTimerUpdateLotsRemaindersTime: function () {
        var start = this.getPresentTime(),
            timeout = 1000;

        this.stopTimerUpdateLotsRemaindersTime();

        function updateTime() {
            var diff, cnt;

            diff = this.getPresentTime() - start - timeout;
            cnt = Math.floor(diff / timeout);

            if (this._remainderTime) {
                setTimeout(this.updateRemainingTime.bind(this, this.addSecondsTo(this._remainderTime, 0 - (cnt + 1))), 0);
                setTimeout(this.signalRemainingTimeUpdate.bind(this, this._remainderTime, this.dataOfLots), 0);
            }

            if (this._serverTime) {
                this._serverTime.setSeconds(this._serverTime.getSeconds(), 0 - (cnt + 1));
                setTimeout(this.signalServerTimeUpdated.bind(this, this._serverTime), 0);
            }

            diff = diff - timeout * cnt;
            start = this.getPresentTime() - diff;

            this.__timerOfUpdateLotsRemainderTime = setTimeout(
                updateTime.bind(this),
                (timeout - diff)
            );

            this.updateLotsRemaindersTime(cnt);
        }

        updateTime.call(this);
    },

    updateRemainingTime: function (remainingTime) {
        this._remainderTime = remainingTime;

        if (this._isTimeOver()) {
            this._remainderTime.Hours = 0;
            this._remainderTime.Minutes = 0;
            this._remainderTime.Seconds = 0;
            this.syncServerTime();
        }
    },

    stopTimerUpdateLotsRemaindersTime: function () {
        clearTimeout(this.__timerOfUpdateLotsRemainderTime);
    },

    /**
     * Проверка завершены ли торги или нет?
     * @param remainingTime {timestamp}
     * @private
     * @return {Boolean}
     */
    _isTimeOver: function (remainingTime) {
        var _remainingTime = remainingTime || this._remainderTime;

        return _remainingTime.Hours <= 0
            && _remainingTime.Minutes <= 0
            && _remainingTime.Seconds <= 0;
    },

    /**
     * Сигнал о завершении торгов
     * @override
     */
    signalTimeIsOver: function () { },

    /**
     * Сигнал о завершении торгов для конкретного лота
     * @override
     */
    signalTimeIsOverForLot: function () { },

    /**
     * Запрос к серверу для получения серверного времени обвалился или пришел неверный ответ
     * @override
     */
    signalServerResponseFail: function () { },

    /**
     * Сигнал о завершении оновления серверного времени
     * @param serverTime {Date} Серверное время
     * @override
     */
    signalServerTimeUpdated: function (serverTime) {
        //this.showServerTime();
        //this.showRemainingTime();
    },

    /**
     * Сигнал обновления остаточного времени
     * @param remainingTime {Object} - остаточное время
     * @override
     */
    signalRemainingTimeUpdate: function (remainingTime, dataLots) {},

    /**
     * Завершено обновление времени лотов
     * @param hashLotsTime
     */
    signalLotsUpdated: function (hashLotsTime) {},

    /**
     * Получить текущее время
     */
    getPresentTime: function () {
        return new Date().getTime();
    },

    /**
     * Добавление секунд к указанному времени
     * @param timeSpan {timestamp} указанное время
     * @param s {Number} сколько секунд надо добавить
     * @returns {timestamp}
     */
    addSecondsTo: function (timeSpan, s) {

        var ts = timeSpan.Hours * 3600;
        ts += timeSpan.Minutes * 60;
        ts += timeSpan.Seconds;

        ts += s;

        var h = ~~(ts / 3600);
        ts = ts % 3600;
        var m = ~~(ts / 60);
        ts = ts % 60;

        timeSpan.Hours = h;
        timeSpan.Minutes = m;
        timeSpan.Seconds = ts;
        return timeSpan;
    }
};
<|endoftext|>


<commit_before>// Copyright © 2014 Intel Corporation. All rights reserved.
// Use  of this  source  code is  governed by  an Apache v2
// license that can be found in the LICENSE-APACHE-V2 file.

var ChildProcess = require('child_process');
var Path = require("path");
var ShellJS = require("shelljs");
var AndroidTargets = require("../src/AndroidTargets");
var Console = require("./Console");

/**
 * Callback signature for {@link AndroidSDK.queryTarget}
 * @param {String} apiTarget SDK API target identifier or null.
 * @param {String} errmsg Error message or null.
 * @memberOf AndroidSDK
 * @inner
 */
function queryTargetCb(apiTarget, errmsg) {}

/**
 * Callback signature for {@link AndroidSDK.generateProjectTemplate}
 * @param {String} path Path of project template or null.
 * @param {String} logmsg Log message or null.
 * @param {String} errmsg Error message or null.
 * @memberOf AndroidSDK
 * @inner
 */
function generateProjectTemplateCb(path, logmsg, errmsg) {}

/**
 * Callback signature for {@link AndroidSDK.buildProject}
 * @param {Boolean} success Whether build succeeded.
 * @memberOf AndroidSDK
 * @inner
 */
function buildProjectCb(success) {}

/**
 * Create AndroidSDK object, wrapping Android cmd-line tool interactions.
 * @throws {AndroidSDK~SDKNotFoundError} If the Android SDK was not found in the environment.
 * @constructor
 */
function AndroidSDK() {

    this._scriptPath = this.findAndroidScriptPath();
    if (this._scriptPath === null) {
        // TODO think out a way to unit test this code path.
        throw new SDKNotFoundError("Android SDK now found in environment search path.");
    }
}

/**
 * Query for lowest API target that supports apiLevel.
 * @function queryTarget
 * @param {Number} apiLevel Minimum supported API level.
 * @param {Function} callback see {@link AndroidSDK~queryTargetCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.queryTarget =
function(apiLevel, callback) {

    if (this.buffer === null) {
        callback([], "Android SDK executable not found");
        return;
    }

    var child = ChildProcess.execFile(this._scriptPath, ["list", "target"], {},
                                      function(errmsg, stdlog, errlog) {

        var apiTarget = null;
        if (stdlog !== null) {

            try {
                var targets = new AndroidTargets(stdlog);
                apiTarget = targets.pickLowest(apiLevel);
            } catch (e) {
                errmsg = "Failed to parse SDK API targets.";
            }

        } else if (errmsg === null) {
            errmsg = "No SDK API targets found";
        }

        callback(apiTarget, errmsg);
    }.bind(this));
};

/**
 * Create project template by running "android create project ..."
 * @function generateProjectTemplate
 * @param {String} packageId Package name in the com.example.Foo format.
 * @param {String} apiTarget Android API target android-xy as per "android list targets".
 * @param {Function} callback see {@link AndroidSDK~generateProjectTemplateCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.generateProjectTemplate =
function(packageId, apiTarget, callback) {

    var errmsg = null;

    // Construct path and fail if exists.
    var wd = ShellJS.pwd();
    var path = wd + Path.sep + packageId;
    if (ShellJS.test("-e", path)) {
        errmsg = "Error: project dir '" + path + "' already exists";
        Console.error(errmsg);
        callback(null, null, errmsg);
        return;
    }

    // Create project
    // "android create project -t android-18 -p $(pwd)/Foo -k com.example.Foo -a MainActivity"
    var args = ["create", "project",
                "-t", apiTarget,
                "-p", path,
                "-k", packageId,
                "-a", "MainActivity"];
    var stdlog = null;
    var errlog = null;
    var child = ChildProcess.execFile(this._scriptPath, args, {},
                                      function(errmsg, stdlog, errlog) {

        if (errlog && !errmsg) {
            // Pass back errlog output as error message.
            errmsg = errlog;
        }

        callback(path, stdlog, errmsg);
        return;
    });
};

AndroidSDK.prototype.refreshProject =
function() {

    // TODO
};

/**
 * Build project by running "ant debug" or "ant release"
 * @function buildProject
 * @param {Boolean} release Whether to build a release or debug package.
 * @param {Function} callback see {@link AndroidSDK~buildProjectCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.buildProject =
function(release, callback) {

    var ant = ShellJS.which("ant");
    if (!ant) {
        callback(null, "Executable 'ant' not found in path");
        return;
    }

    var exitStatus = { "code" : 0 };
    var args = [ release ? "release" : "debug" ];
    var child = ChildProcess.execFile(ant, args);

    child.stdout.on("data", function(data) {
        Console.put(data);
    });

    child.stderr.on("data", function(data) {
        Console.put(data, true);
    });

    child.on("exit", function(code, signal) {
        callback(code === 0);
        return;
    });
};

AndroidSDK.prototype.findAndroidScriptPath =
function() {

    return ShellJS.which("android");
};

/**
 * Creates a new SDKNotFoundError.
 * @extends Error
 * @param {String} message Error message.
 * @constructor
 * @memberOf AndroidSDK
 * @inner
 */
function SDKNotFoundError(message) {
    Error.call(this, message);
}
SDKNotFoundError.prototype = Error.prototype;

AndroidSDK.SDKNotFoundError = SDKNotFoundError;

module.exports = AndroidSDK;
<commit_msg>doc: Updates for AndroidSDK.findAndroidScriptPath()<commit_after>// Copyright © 2014 Intel Corporation. All rights reserved.
// Use  of this  source  code is  governed by  an Apache v2
// license that can be found in the LICENSE-APACHE-V2 file.

var ChildProcess = require('child_process');
var Path = require("path");
var ShellJS = require("shelljs");
var AndroidTargets = require("../src/AndroidTargets");
var Console = require("./Console");

/**
 * Callback signature for {@link AndroidSDK.queryTarget}
 * @param {String} apiTarget SDK API target identifier or null.
 * @param {String} errmsg Error message or null.
 * @memberOf AndroidSDK
 * @inner
 */
function queryTargetCb(apiTarget, errmsg) {}

/**
 * Callback signature for {@link AndroidSDK.generateProjectTemplate}
 * @param {String} path Path of project template or null.
 * @param {String} logmsg Log message or null.
 * @param {String} errmsg Error message or null.
 * @memberOf AndroidSDK
 * @inner
 */
function generateProjectTemplateCb(path, logmsg, errmsg) {}

/**
 * Callback signature for {@link AndroidSDK.buildProject}
 * @param {Boolean} success Whether build succeeded.
 * @memberOf AndroidSDK
 * @inner
 */
function buildProjectCb(success) {}

/**
 * Create AndroidSDK object, wrapping Android cmd-line tool interactions.
 * @throws {AndroidSDK~SDKNotFoundError} If the Android SDK was not found in the environment.
 * @constructor
 */
function AndroidSDK() {

    this._scriptPath = this.findAndroidScriptPath();
    if (this._scriptPath === null) {
        // TODO think out a way to unit test this code path.
        throw new SDKNotFoundError("Android SDK now found in environment search path.");
    }
}

/**
 * Query for lowest API target that supports apiLevel.
 * @function queryTarget
 * @param {Number} apiLevel Minimum supported API level.
 * @param {Function} callback see {@link AndroidSDK~queryTargetCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.queryTarget =
function(apiLevel, callback) {

    if (this.buffer === null) {
        callback([], "Android SDK executable not found");
        return;
    }

    var child = ChildProcess.execFile(this._scriptPath, ["list", "target"], {},
                                      function(errmsg, stdlog, errlog) {

        var apiTarget = null;
        if (stdlog !== null) {

            try {
                var targets = new AndroidTargets(stdlog);
                apiTarget = targets.pickLowest(apiLevel);
            } catch (e) {
                errmsg = "Failed to parse SDK API targets.";
            }

        } else if (errmsg === null) {
            errmsg = "No SDK API targets found";
        }

        callback(apiTarget, errmsg);
    }.bind(this));
};

/**
 * Create project template by running "android create project ..."
 * @function generateProjectTemplate
 * @param {String} packageId Package name in the com.example.Foo format.
 * @param {String} apiTarget Android API target android-xy as per "android list targets".
 * @param {Function} callback see {@link AndroidSDK~generateProjectTemplateCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.generateProjectTemplate =
function(packageId, apiTarget, callback) {

    var errmsg = null;

    // Construct path and fail if exists.
    var wd = ShellJS.pwd();
    var path = wd + Path.sep + packageId;
    if (ShellJS.test("-e", path)) {
        errmsg = "Error: project dir '" + path + "' already exists";
        Console.error(errmsg);
        callback(null, null, errmsg);
        return;
    }

    // Create project
    // "android create project -t android-18 -p $(pwd)/Foo -k com.example.Foo -a MainActivity"
    var args = ["create", "project",
                "-t", apiTarget,
                "-p", path,
                "-k", packageId,
                "-a", "MainActivity"];
    var stdlog = null;
    var errlog = null;
    var child = ChildProcess.execFile(this._scriptPath, args, {},
                                      function(errmsg, stdlog, errlog) {

        if (errlog && !errmsg) {
            // Pass back errlog output as error message.
            errmsg = errlog;
        }

        callback(path, stdlog, errmsg);
        return;
    });
};

AndroidSDK.prototype.refreshProject =
function() {

    // TODO
};

/**
 * Build project by running "ant debug" or "ant release"
 * @function buildProject
 * @param {Boolean} release Whether to build a release or debug package.
 * @param {Function} callback see {@link AndroidSDK~buildProjectCb}
 * @returns null
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.buildProject =
function(release, callback) {

    var ant = ShellJS.which("ant");
    if (!ant) {
        callback(null, "Executable 'ant' not found in path");
        return;
    }

    var exitStatus = { "code" : 0 };
    var args = [ release ? "release" : "debug" ];
    var child = ChildProcess.execFile(ant, args);

    child.stdout.on("data", function(data) {
        Console.put(data);
    });

    child.stderr.on("data", function(data) {
        Console.put(data, true);
    });

    child.on("exit", function(code, signal) {
        callback(code === 0);
        return;
    });
};

/**
 * Try to find the "android" executable in the environment's search path.
 * @function findAndroidScriptPath
 * @returns {String} Path or null.
 * @memberOf AndroidSDK
 */
AndroidSDK.prototype.findAndroidScriptPath =
function() {

    return ShellJS.which("android");
};

/**
 * Creates a new SDKNotFoundError.
 * @extends Error
 * @param {String} message Error message.
 * @constructor
 * @memberOf AndroidSDK
 * @inner
 */
function SDKNotFoundError(message) {
    Error.call(this, message);
}
SDKNotFoundError.prototype = Error.prototype;

AndroidSDK.SDKNotFoundError = SDKNotFoundError;

module.exports = AndroidSDK;
<|endoftext|>


<commit_before>global.__jymfony = global.__jymfony || {};

const Finder = require('./Finder');
const path   = require('path');
const fs     = require('fs');

let createProxy = function (baseDir) {
    return new Proxy({}, {
        get: function (target, name) {
            if (typeof name === 'symbol') {
                return undefined;
            }

            if (target[name] === undefined) {
                let fileName = baseDir + name;

                let stat = undefined;
                for (let i = 2;;) {
                    try {
                        stat = fs.statSync(fileName);
                        break;
                    } catch (e) {
                        if (--i) {
                            fileName += '.js';
                            continue;
                        }

                        break;
                    }
                }

                if (! stat) {
                    return undefined;
                }

                if (stat.isDirectory()) {
                    target[name] = createProxy(baseDir + name + '/');
                } else {
                    target[name] = require(fileName);
                }
            }

            return target[name];
        }
    })
};

module.exports = class Autoloader {
    constructor(finder = null) {
        if (global.__jymfony.autoload) {
            return;
        }

        if (! finder) {
            finder = new Finder();
        }

        this._registered = false;
        this._finder = finder;
        global.__jymfony.autoload = this;
    }

    register() {
        if (this._registered) {
            return;
        }

        this._registered = true;

        let rootDir = this._finder.findRoot();
        for (let module of this._finder.listModules()) {
            let packageInfo;

            try {
                packageInfo = require(module + path.sep + 'package.json');
            } catch (e) {
                continue;
            }

            let dir = path.join(rootDir, 'node_modules', module);
            this._processPackageInfo(packageInfo, dir);
        }

        // let relativeRoot = path.relative(path.dirname(module.filename), rootDir);
        this._processPackageInfo(require(rootDir + '/package.json'), rootDir);
    }

    _processPackageInfo(packageInfo, baseDir) {
        if (! packageInfo.config || ! packageInfo.config['jymfony-autoload']) {
            return;
        }

        let config = packageInfo.config['jymfony-autoload'];
        if (config.namespaces) {
            this._processNamespaces(config.namespaces, baseDir);
        }

        if (config.includes) {
            this._processIncludes(config.includes, baseDir);
        }
    }

    _processNamespaces(config, baseDir) {
        for (let namespace in config) {
            if (! config.hasOwnProperty(namespace)) {
                continue;
            }

            let parts = namespace.split('.');
            let last = parts.pop();
            let parent = global;

            for (let part of parts) {
                parent = this._ensureNamespace(part, parent);
            }

            parent[last] = createProxy(baseDir + '/' + config[namespace]);
        }
    }

    _processIncludes(config, baseDir) {
        for (let fileName of config) {
            require(baseDir + '/' + fileName);
        }
    }

    _ensureNamespace(namespace, parent = global) {
        if (parent[namespace] === undefined) {
            return parent[namespace] = {};
        }

        return parent[namespace];
    }
};
<commit_msg>[Autoloader] allow to define global object (for testing)<commit_after>global.__jymfony = global.__jymfony || {};

const Finder = require('./Finder');
const path   = require('path');
const fs     = require('fs');

let createProxy = function (baseDir) {
    return new Proxy({}, {
        get: function (target, name) {
            if (typeof name === 'symbol') {
                return undefined;
            }

            if (target[name] === undefined) {
                let fileName = baseDir + name;

                let stat = undefined;
                for (let i = 2;;) {
                    try {
                        stat = fs.statSync(fileName);
                        break;
                    } catch (e) {
                        if (--i) {
                            fileName += '.js';
                            continue;
                        }

                        break;
                    }
                }

                if (! stat) {
                    return undefined;
                }

                if (stat.isDirectory()) {
                    target[name] = createProxy(baseDir + name + '/');
                } else {
                    target[name] = require(fileName);
                }
            }

            return target[name];
        }
    })
};

module.exports = class Autoloader {
    constructor(finder = null, globalObject = global) {
        if (globalObject.__jymfony.autoload) {
            return;
        }

        if (! finder) {
            finder = new Finder();
        }

        this._registered = false;
        this._finder = finder;
        this._global = globalObject;
        this._global.__jymfony.autoload = this;
    }

    register() {
        if (this._registered) {
            return;
        }

        this._registered = true;

        let rootDir = this._finder.findRoot();
        for (let module of this._finder.listModules()) {
            let packageInfo;

            try {
                packageInfo = require(module + path.sep + 'package.json');
            } catch (e) {
                continue;
            }

            let dir = path.join(rootDir, 'node_modules', module);
            this._processPackageInfo(packageInfo, dir);
        }

        // let relativeRoot = path.relative(path.dirname(module.filename), rootDir);
        this._processPackageInfo(require(rootDir + '/package.json'), rootDir);
    }

    _processPackageInfo(packageInfo, baseDir) {
        if (! packageInfo.config || ! packageInfo.config['jymfony-autoload']) {
            return;
        }

        let config = packageInfo.config['jymfony-autoload'];
        if (config.namespaces) {
            this._processNamespaces(config.namespaces, baseDir);
        }

        if (config.includes) {
            this._processIncludes(config.includes, baseDir);
        }
    }

    _processNamespaces(config, baseDir) {
        for (let namespace in config) {
            if (! config.hasOwnProperty(namespace)) {
                continue;
            }

            let parts = namespace.split('.');
            let last = parts.pop();
            let parent = this._global;

            for (let part of parts) {
                parent = this._ensureNamespace(part, parent);
            }

            parent[last] = createProxy(baseDir + '/' + config[namespace]);
        }
    }

    _processIncludes(config, baseDir) {
        for (let fileName of config) {
            require(baseDir + '/' + fileName);
        }
    }

    _ensureNamespace(namespace, parent = this._global) {
        if (parent[namespace] === undefined) {
            return parent[namespace] = {};
        }

        return parent[namespace];
    }
};
<|endoftext|>


<commit_before>(function(){
	"use strict";

	var root = this,
		Chart = root.Chart,
		helpers = Chart.helpers;

	var defaultConfig = {

		///Boolean - Whether grid lines are shown across the chart
		scaleShowGridLines : true,

		//String - Colour of the grid lines
		scaleGridLineColor : "rgba(0,0,0,.05)",

		//Number - Width of the grid lines
		scaleGridLineWidth : 1,

		//Boolean - Whether the line is curved between points
		bezierCurve : true,

		//Number - Tension of the bezier curve between points
		bezierCurveTension : 0.4,

		//Boolean - Whether to show a dot for each point
		pointDot : true,

		//Number - Radius of each point dot in pixels
		pointDotRadius : 4,

		//Number - Pixel width of point dot stroke
		pointDotStrokeWidth : 1,

		//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
		pointHitDetectionRadius : 20,

		//Boolean - Whether to show a stroke for datasets
		datasetStroke : true,

		//Number - Pixel width of dataset stroke
		datasetStrokeWidth : 2,

		//Boolean - Whether to fill the dataset with a colour
		datasetFill : true,

		//String - A legend template
		legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%>
<span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
<%}%>
"

	};

	Chart.Type.extend({
		name: "Line",
		defaults : defaultConfig,
		initialize:  function(data){
			//Declare the extension of the default point, to cater for the options passed in to the constructor
			this.PointClass = Chart.Point.extend({
				strokeWidth : this.options.pointDotStrokeWidth,
				radius : this.options.pointDotRadius,
				display: this.options.pointDot,
				hitDetectionRadius : this.options.pointHitDetectionRadius,
				ctx : this.chart.ctx,
				inRange : function(mouseX){
					return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
				}
			});

			this.datasets = [];

			//Set up tooltip events on the chart
			if (this.options.showTooltips){
				helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
					var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
					this.eachPoints(function(point){
						point.restore(['fillColor', 'strokeColor']);
					});
					helpers.each(activePoints, function(activePoint){
						activePoint.fillColor = activePoint.highlightFill;
						activePoint.strokeColor = activePoint.highlightStroke;
					});
					this.showTooltip(activePoints);
				});
			}

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(data.datasets,function(dataset){

				var datasetObject = {
					label : dataset.label || null,
					fillColor : dataset.fillColor,
					strokeColor : dataset.strokeColor,
					pointColor : dataset.pointColor,
					pointStrokeColor : dataset.pointStrokeColor,
					points : []
				};

				this.datasets.push(datasetObject);

				helpers.each(dataset.data,function(dataPoint,index){
					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.points.push(new this.PointClass({
						value : dataPoint,
						label : data.labels[index],
						datasetLabel: dataset.label,
						strokeColor : dataset.pointStrokeColor,
						fillColor : dataset.pointColor,
						highlightFill : dataset.pointHighlightFill || dataset.pointColor,
						highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
					}));
				},this);

				this.buildScale(data.labels);

				this.eachPoints(function(point, index){
					helpers.extend(point, {
						x: this.scale.calculateX(index),
						y: this.scale.endPoint
					});
					point.save();
				}, this);

			},this);

			this.render();
		},
		update : function(){
			this.scale.update();
			// Reset any highlight colours before updating.
			helpers.each(this.activeElements, function(activeElement){
				activeElement.restore(['fillColor', 'strokeColor']);
			});
			this.eachPoints(function(point){
				point.save();
			});
			this.render();
		},
		eachPoints : function(callback){
			helpers.each(this.datasets,function(dataset){
				helpers.each(dataset.points,callback,this);
			},this);
		},
		getPointsAtEvent : function(e){
			var pointsArray = [],
				eventPosition = helpers.getRelativePosition(e);
			helpers.each(this.datasets,function(dataset){
				helpers.each(dataset.points,function(point){
					if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
				});
			},this);
			return pointsArray;
		},
		buildScale : function(labels){
			var self = this;

			var dataTotal = function(){
				var values = [];
				self.eachPoints(function(point){
					values.push(point.value);
				});

				return values;
			};

			var scaleOptions = {
				templateString : this.options.scaleLabel,
				height : this.chart.height,
				width : this.chart.width,
				ctx : this.chart.ctx,
				textColor : this.options.scaleFontColor,
				fontSize : this.options.scaleFontSize,
				fontStyle : this.options.scaleFontStyle,
				fontFamily : this.options.scaleFontFamily,
				valuesCount : labels.length,
				beginAtZero : this.options.scaleBeginAtZero,
				integersOnly : this.options.scaleIntegersOnly,
				calculateYRange : function(currentHeight){
					var updatedRanges = helpers.calculateScaleRange(
						dataTotal(),
						currentHeight,
						this.fontSize,
						this.beginAtZero,
						this.integersOnly
					);
					helpers.extend(this, updatedRanges);
				},
				xLabels : labels,
				font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
				lineWidth : this.options.scaleLineWidth,
				lineColor : this.options.scaleLineColor,
				gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
				gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
				padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
				showLabels : this.options.scaleShowLabels,
				display : this.options.showScale
			};

			if (this.options.scaleOverride){
				helpers.extend(scaleOptions, {
					calculateYRange: helpers.noop,
					steps: this.options.scaleSteps,
					stepValue: this.options.scaleStepWidth,
					min: this.options.scaleStartValue,
					max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
				});
			}

			this.scale = new Chart.Scale(scaleOptions);
		},
		addData : function(valuesArray,label){
			//Map the values array for each of the datasets

			helpers.each(valuesArray,function(value,datasetIndex){
				//Add a new point for each piece of data, passing any required data to draw.
				this.datasets[datasetIndex].points.push(new this.PointClass({
					value : value,
					label : label,
					x: this.scale.calculateX(this.scale.valuesCount+1),
					y: this.scale.endPoint,
					strokeColor : this.datasets[datasetIndex].pointStrokeColor,
					fillColor : this.datasets[datasetIndex].pointColor
				}));
			},this);

			this.scale.addXLabel(label);
			//Then re-render the chart.
			this.update();
		},
		removeData : function(){
			this.scale.removeXLabel();
			//Then re-render the chart.
			helpers.each(this.datasets,function(dataset){
				dataset.points.shift();
			},this);
			this.update();
		},
		reflow : function(){
			var newScaleProps = helpers.extend({
				height : this.chart.height,
				width : this.chart.width
			});
			this.scale.update(newScaleProps);
		},
		draw : function(ease){
			var easingDecimal = ease || 1;
			this.clear();

			var ctx = this.chart.ctx;

			// Some helper methods for getting the next/prev points
			var hasValue = function(item){
				return item.value !== null;
			},
			nextPoint = function(point, collection, index){
				return helpers.findNextWhere(collection, hasValue, index) || point;
			},
			previousPoint = function(point, collection, index){
				return helpers.findPreviousWhere(collection, hasValue, index) || point;
			};

			this.scale.draw(easingDecimal);

			helpers.each(this.datasets,function(dataset){
				var pointsWithValues = helpers.where(dataset.points, hasValue);

				//Transition each point first so that the line and point drawing isn't out of sync
				//We can use this extra loop to calculate the control points of this dataset also in this loop

				helpers.each(dataset.points, function(point, index){
					if (point.hasValue()){
						point.transition({
							y : this.scale.calculateY(point.value),
							x : this.scale.calculateX(index)
						}, easingDecimal);
					}
				},this);

				// Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
				// This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
				if (this.options.bezierCurve){
					helpers.each(pointsWithValues, function(point, index){
						var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
						point.controlPoints = helpers.splineCurve(
							previousPoint(point, pointsWithValues, index),
							point,
							nextPoint(point, pointsWithValues, index),
							tension
						);
					},this);
				}

				//Draw the line between all the points
				ctx.lineWidth = this.options.datasetStrokeWidth;
				ctx.strokeStyle = dataset.strokeColor;
				ctx.beginPath();

				helpers.each(pointsWithValues, function(point, index){
					if (index === 0){
						ctx.moveTo(point.x, point.y);
					}
					else{
						if(this.options.bezierCurve){
							var previous = previousPoint(point, pointsWithValues, index);

							ctx.bezierCurveTo(
								previous.controlPoints.outer.x,
								previous.controlPoints.outer.y,
								point.controlPoints.inner.x,
								point.controlPoints.inner.y,
								point.x,
								point.y
							);
						}
						else{
							ctx.lineTo(point.x,point.y);
						}
					}
				}, this);

				ctx.stroke();

				if (this.options.datasetFill && pointsWithValues.length > 0){
					//Round off the line by going to the base of the chart, back to the start, then fill.
					ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
					ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
					ctx.fillStyle = dataset.fillColor;
					ctx.closePath();
					ctx.fill();
				}

				//Now draw the points over the line
				//A little inefficient double looping, but better than the line
				//lagging behind the point positions
				helpers.each(pointsWithValues,function(point){
					point.draw();
				});
			},this);
		}
	});

}).call(this);<commit_msg>Cap bezier handles inside the scale<commit_after>(function(){
	"use strict";

	var root = this,
		Chart = root.Chart,
		helpers = Chart.helpers;

	var defaultConfig = {

		///Boolean - Whether grid lines are shown across the chart
		scaleShowGridLines : true,

		//String - Colour of the grid lines
		scaleGridLineColor : "rgba(0,0,0,.05)",

		//Number - Width of the grid lines
		scaleGridLineWidth : 1,

		//Boolean - Whether the line is curved between points
		bezierCurve : true,

		//Number - Tension of the bezier curve between points
		bezierCurveTension : 0.4,

		//Boolean - Whether to show a dot for each point
		pointDot : true,

		//Number - Radius of each point dot in pixels
		pointDotRadius : 4,

		//Number - Pixel width of point dot stroke
		pointDotStrokeWidth : 1,

		//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
		pointHitDetectionRadius : 20,

		//Boolean - Whether to show a stroke for datasets
		datasetStroke : true,

		//Number - Pixel width of dataset stroke
		datasetStrokeWidth : 2,

		//Boolean - Whether to fill the dataset with a colour
		datasetFill : true,

		//String - A legend template
		legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%>
<span style=\"background-color:<%=datasets[i].strokeColor%>\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%>
<%}%>
"

	};

	Chart.Type.extend({
		name: "Line",
		defaults : defaultConfig,
		initialize:  function(data){
			//Declare the extension of the default point, to cater for the options passed in to the constructor
			this.PointClass = Chart.Point.extend({
				strokeWidth : this.options.pointDotStrokeWidth,
				radius : this.options.pointDotRadius,
				display: this.options.pointDot,
				hitDetectionRadius : this.options.pointHitDetectionRadius,
				ctx : this.chart.ctx,
				inRange : function(mouseX){
					return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
				}
			});

			this.datasets = [];

			//Set up tooltip events on the chart
			if (this.options.showTooltips){
				helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
					var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
					this.eachPoints(function(point){
						point.restore(['fillColor', 'strokeColor']);
					});
					helpers.each(activePoints, function(activePoint){
						activePoint.fillColor = activePoint.highlightFill;
						activePoint.strokeColor = activePoint.highlightStroke;
					});
					this.showTooltip(activePoints);
				});
			}

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(data.datasets,function(dataset){

				var datasetObject = {
					label : dataset.label || null,
					fillColor : dataset.fillColor,
					strokeColor : dataset.strokeColor,
					pointColor : dataset.pointColor,
					pointStrokeColor : dataset.pointStrokeColor,
					points : []
				};

				this.datasets.push(datasetObject);

				helpers.each(dataset.data,function(dataPoint,index){
					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.points.push(new this.PointClass({
						value : dataPoint,
						label : data.labels[index],
						datasetLabel: dataset.label,
						strokeColor : dataset.pointStrokeColor,
						fillColor : dataset.pointColor,
						highlightFill : dataset.pointHighlightFill || dataset.pointColor,
						highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
					}));
				},this);

				this.buildScale(data.labels);

				this.eachPoints(function(point, index){
					helpers.extend(point, {
						x: this.scale.calculateX(index),
						y: this.scale.endPoint
					});
					point.save();
				}, this);

			},this);

			this.render();
		},
		update : function(){
			this.scale.update();
			// Reset any highlight colours before updating.
			helpers.each(this.activeElements, function(activeElement){
				activeElement.restore(['fillColor', 'strokeColor']);
			});
			this.eachPoints(function(point){
				point.save();
			});
			this.render();
		},
		eachPoints : function(callback){
			helpers.each(this.datasets,function(dataset){
				helpers.each(dataset.points,callback,this);
			},this);
		},
		getPointsAtEvent : function(e){
			var pointsArray = [],
				eventPosition = helpers.getRelativePosition(e);
			helpers.each(this.datasets,function(dataset){
				helpers.each(dataset.points,function(point){
					if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
				});
			},this);
			return pointsArray;
		},
		buildScale : function(labels){
			var self = this;

			var dataTotal = function(){
				var values = [];
				self.eachPoints(function(point){
					values.push(point.value);
				});

				return values;
			};

			var scaleOptions = {
				templateString : this.options.scaleLabel,
				height : this.chart.height,
				width : this.chart.width,
				ctx : this.chart.ctx,
				textColor : this.options.scaleFontColor,
				fontSize : this.options.scaleFontSize,
				fontStyle : this.options.scaleFontStyle,
				fontFamily : this.options.scaleFontFamily,
				valuesCount : labels.length,
				beginAtZero : this.options.scaleBeginAtZero,
				integersOnly : this.options.scaleIntegersOnly,
				calculateYRange : function(currentHeight){
					var updatedRanges = helpers.calculateScaleRange(
						dataTotal(),
						currentHeight,
						this.fontSize,
						this.beginAtZero,
						this.integersOnly
					);
					helpers.extend(this, updatedRanges);
				},
				xLabels : labels,
				font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
				lineWidth : this.options.scaleLineWidth,
				lineColor : this.options.scaleLineColor,
				gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
				gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
				padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
				showLabels : this.options.scaleShowLabels,
				display : this.options.showScale
			};

			if (this.options.scaleOverride){
				helpers.extend(scaleOptions, {
					calculateYRange: helpers.noop,
					steps: this.options.scaleSteps,
					stepValue: this.options.scaleStepWidth,
					min: this.options.scaleStartValue,
					max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
				});
			}

			this.scale = new Chart.Scale(scaleOptions);
		},
		addData : function(valuesArray,label){
			//Map the values array for each of the datasets

			helpers.each(valuesArray,function(value,datasetIndex){
				//Add a new point for each piece of data, passing any required data to draw.
				this.datasets[datasetIndex].points.push(new this.PointClass({
					value : value,
					label : label,
					x: this.scale.calculateX(this.scale.valuesCount+1),
					y: this.scale.endPoint,
					strokeColor : this.datasets[datasetIndex].pointStrokeColor,
					fillColor : this.datasets[datasetIndex].pointColor
				}));
			},this);

			this.scale.addXLabel(label);
			//Then re-render the chart.
			this.update();
		},
		removeData : function(){
			this.scale.removeXLabel();
			//Then re-render the chart.
			helpers.each(this.datasets,function(dataset){
				dataset.points.shift();
			},this);
			this.update();
		},
		reflow : function(){
			var newScaleProps = helpers.extend({
				height : this.chart.height,
				width : this.chart.width
			});
			this.scale.update(newScaleProps);
		},
		draw : function(ease){
			var easingDecimal = ease || 1;
			this.clear();

			var ctx = this.chart.ctx;

			// Some helper methods for getting the next/prev points
			var hasValue = function(item){
				return item.value !== null;
			},
			nextPoint = function(point, collection, index){
				return helpers.findNextWhere(collection, hasValue, index) || point;
			},
			previousPoint = function(point, collection, index){
				return helpers.findPreviousWhere(collection, hasValue, index) || point;
			};

			this.scale.draw(easingDecimal);

			helpers.each(this.datasets,function(dataset){
				var pointsWithValues = helpers.where(dataset.points, hasValue);

				//Transition each point first so that the line and point drawing isn't out of sync
				//We can use this extra loop to calculate the control points of this dataset also in this loop

				helpers.each(dataset.points, function(point, index){
					if (point.hasValue()){
						point.transition({
							y : this.scale.calculateY(point.value),
							x : this.scale.calculateX(index)
						}, easingDecimal);
					}
				},this);

				// Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
				// This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
				if (this.options.bezierCurve){
					helpers.each(pointsWithValues, function(point, index){
						var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
						point.controlPoints = helpers.splineCurve(
							previousPoint(point, pointsWithValues, index),
							point,
							nextPoint(point, pointsWithValues, index),
							tension
						);

						// Prevent the bezier going outside of the bounds of the graph

						// Cap puter bezier handles to the upper/lower scale bounds
						if (point.controlPoints.outer.y > this.scale.endPoint){
							point.controlPoints.outer.y = this.scale.endPoint;
						}
						else if (point.controlPoints.outer.y < this.scale.startPoint){
							point.controlPoints.outer.y = this.scale.startPoint;
						}

						// Cap inner bezier handles to the upper/lower scale bounds
						if (point.controlPoints.inner.y > this.scale.endPoint){
							point.controlPoints.inner.y = this.scale.endPoint;
						}
						else if (point.controlPoints.inner.y < this.scale.startPoint){
							point.controlPoints.inner.y = this.scale.startPoint;
						}
					},this);
				}

				//Draw the line between all the points
				ctx.lineWidth = this.options.datasetStrokeWidth;
				ctx.strokeStyle = dataset.strokeColor;
				ctx.beginPath();

				helpers.each(pointsWithValues, function(point, index){
					if (index === 0){
						ctx.moveTo(point.x, point.y);
					}
					else{
						if(this.options.bezierCurve){
							var previous = previousPoint(point, pointsWithValues, index);

							ctx.bezierCurveTo(
								previous.controlPoints.outer.x,
								previous.controlPoints.outer.y,
								point.controlPoints.inner.x,
								point.controlPoints.inner.y,
								point.x,
								point.y
							);
						}
						else{
							ctx.lineTo(point.x,point.y);
						}
					}
				}, this);

				ctx.stroke();

				if (this.options.datasetFill && pointsWithValues.length > 0){
					//Round off the line by going to the base of the chart, back to the start, then fill.
					ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
					ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
					ctx.fillStyle = dataset.fillColor;
					ctx.closePath();
					ctx.fill();
				}

				//Now draw the points over the line
				//A little inefficient double looping, but better than the line
				//lagging behind the point positions
				helpers.each(pointsWithValues,function(point){
					point.draw();
				});
			},this);
		}
	});

}).call(this);<|endoftext|>


<commit_before>const React = require('react');
const ReactDOM = require('react-dom');
const PropTypes = require('prop-types');
const className = require('classnames');
const debounce = require('lodash.debounce');
const isEqual = require('lodash.isequal');
const createReactClass = require('create-react-class');

function normalizeLineEndings (str) {
	if (!str) return str;
	return str.replace(/\r\n|\r/g, '\n');
}

const CodeMirror = createReactClass({
	propTypes: {
		autoFocus: PropTypes.bool,
		className: PropTypes.any,
		codeMirrorInstance: PropTypes.func,
		defaultValue: PropTypes.string,
		onChange: PropTypes.func,
		onCursorActivity: PropTypes.func,
		onFocusChange: PropTypes.func,
		onScroll: PropTypes.func,
		options: PropTypes.object,
		path: PropTypes.string,
		value: PropTypes.string,
		preserveScrollPosition: PropTypes.bool,
	},
	getDefaultProps () {
		return {
			preserveScrollPosition: false,
		};
	},
	getCodeMirrorInstance () {
		return this.props.codeMirrorInstance || require('codemirror');
	},
	getInitialState () {
		return {
			isFocused: false,
		};
	},
	componentWillMount () {
		this.componentWillReceiveProps = debounce(this.componentWillReceiveProps, 0);
	},
	componentDidMount () {
		const codeMirrorInstance = this.getCodeMirrorInstance();
		this.codeMirror = codeMirrorInstance.fromTextArea(this.textareaNode, this.props.options);
		this.codeMirror.on('change', this.codemirrorValueChanged);
		this.codeMirror.on('cursorActivity', this.cursorActivity);
		this.codeMirror.on('focus', this.focusChanged.bind(this, true));
		this.codeMirror.on('blur', this.focusChanged.bind(this, false));
		this.codeMirror.on('scroll', this.scrollChanged);
		this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
	},
	componentWillUnmount () {
		// is there a lighter-weight way to remove the cm instance?
		if (this.codeMirror) {
			this.codeMirror.toTextArea();
		}
	},
	componentWillReceiveProps: function (nextProps) {
		if (this.codeMirror && nextProps.value !== undefined && nextProps.value !== this.props.value && normalizeLineEndings(this.codeMirror.getValue()) !== normalizeLineEndings(nextProps.value)) {
			if (this.props.preserveScrollPosition) {
				var prevScrollPosition = this.codeMirror.getScrollInfo();
				this.codeMirror.setValue(nextProps.value);
				this.codeMirror.scrollTo(prevScrollPosition.left, prevScrollPosition.top);
			} else {
				this.codeMirror.setValue(nextProps.value);
			}
		}
		if (typeof nextProps.options === 'object') {
			for (let optionName in nextProps.options) {
				if (nextProps.options.hasOwnProperty(optionName)) {
					this.setOptionIfChanged(optionName, nextProps.options[optionName]);
				}
			}
		}
	},
	setOptionIfChanged (optionName, newValue) {
 		const oldValue = this.codeMirror.getOption(optionName);
 		if (!isEqual(oldValue, newValue)) {
 			this.codeMirror.setOption(optionName, newValue);
 		}
 	},
	getCodeMirror () {
		return this.codeMirror;
	},
	focus () {
		if (this.codeMirror) {
			this.codeMirror.focus();
		}
	},
	focusChanged (focused) {
		this.setState({
			isFocused: focused,
		});
		this.props.onFocusChange && this.props.onFocusChange(focused);
	},
	cursorActivity (cm) {
		this.props.onCursorActivity && this.props.onCursorActivity(cm);
	},
	scrollChanged (cm) {
		this.props.onScroll && this.props.onScroll(cm.getScrollInfo());
	},
	codemirrorValueChanged (doc, change) {
		if (this.props.onChange && change.origin !== 'setValue') {
			this.props.onChange(doc.getValue(), change);
		}
	},
	render () {
		const editorClassName = className(
			'ReactCodeMirror',
			this.state.isFocused ? 'ReactCodeMirror--focused' : null,
			this.props.className
		);
		return (

				<textarea
					ref={ref => this.textareaNode = ref}
					name={this.props.path}
					defaultValue={this.props.value}
					autoComplete="off"
					autoFocus={this.props.autoFocus}
				/>

		);
	},
});

module.exports = CodeMirror;
<commit_msg>Add name prop, deprecate path with warning<commit_after>const React = require('react');
const ReactDOM = require('react-dom');
const PropTypes = require('prop-types');
const className = require('classnames');
const debounce = require('lodash.debounce');
const isEqual = require('lodash.isequal');
const createReactClass = require('create-react-class');

function normalizeLineEndings (str) {
	if (!str) return str;
	return str.replace(/\r\n|\r/g, '\n');
}

const CodeMirror = createReactClass({
	propTypes: {
		autoFocus: PropTypes.bool,
		className: PropTypes.any,
		codeMirrorInstance: PropTypes.func,
		defaultValue: PropTypes.string,
		name: PropTypes.string,
		onChange: PropTypes.func,
		onCursorActivity: PropTypes.func,
		onFocusChange: PropTypes.func,
		onScroll: PropTypes.func,
		options: PropTypes.object,
		path: PropTypes.string,
		value: PropTypes.string,
		preserveScrollPosition: PropTypes.bool,
	},
	getDefaultProps () {
		return {
			preserveScrollPosition: false,
		};
	},
	getCodeMirrorInstance () {
		return this.props.codeMirrorInstance || require('codemirror');
	},
	getInitialState () {
		return {
			isFocused: false,
		};
	},
	componentWillMount () {
		this.componentWillReceiveProps = debounce(this.componentWillReceiveProps, 0);
		if (this.props.path) {
			console.error('Warning: react-codemirror: the `path` prop has been changed to `name`');
		}
	},
	componentDidMount () {
		const codeMirrorInstance = this.getCodeMirrorInstance();
		this.codeMirror = codeMirrorInstance.fromTextArea(this.textareaNode, this.props.options);
		this.codeMirror.on('change', this.codemirrorValueChanged);
		this.codeMirror.on('cursorActivity', this.cursorActivity);
		this.codeMirror.on('focus', this.focusChanged.bind(this, true));
		this.codeMirror.on('blur', this.focusChanged.bind(this, false));
		this.codeMirror.on('scroll', this.scrollChanged);
		this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
	},
	componentWillUnmount () {
		// is there a lighter-weight way to remove the cm instance?
		if (this.codeMirror) {
			this.codeMirror.toTextArea();
		}
	},
	componentWillReceiveProps: function (nextProps) {
		if (this.codeMirror && nextProps.value !== undefined && nextProps.value !== this.props.value && normalizeLineEndings(this.codeMirror.getValue()) !== normalizeLineEndings(nextProps.value)) {
			if (this.props.preserveScrollPosition) {
				var prevScrollPosition = this.codeMirror.getScrollInfo();
				this.codeMirror.setValue(nextProps.value);
				this.codeMirror.scrollTo(prevScrollPosition.left, prevScrollPosition.top);
			} else {
				this.codeMirror.setValue(nextProps.value);
			}
		}
		if (typeof nextProps.options === 'object') {
			for (let optionName in nextProps.options) {
				if (nextProps.options.hasOwnProperty(optionName)) {
					this.setOptionIfChanged(optionName, nextProps.options[optionName]);
				}
			}
		}
	},
	setOptionIfChanged (optionName, newValue) {
 		const oldValue = this.codeMirror.getOption(optionName);
 		if (!isEqual(oldValue, newValue)) {
 			this.codeMirror.setOption(optionName, newValue);
 		}
 	},
	getCodeMirror () {
		return this.codeMirror;
	},
	focus () {
		if (this.codeMirror) {
			this.codeMirror.focus();
		}
	},
	focusChanged (focused) {
		this.setState({
			isFocused: focused,
		});
		this.props.onFocusChange && this.props.onFocusChange(focused);
	},
	cursorActivity (cm) {
		this.props.onCursorActivity && this.props.onCursorActivity(cm);
	},
	scrollChanged (cm) {
		this.props.onScroll && this.props.onScroll(cm.getScrollInfo());
	},
	codemirrorValueChanged (doc, change) {
		if (this.props.onChange && change.origin !== 'setValue') {
			this.props.onChange(doc.getValue(), change);
		}
	},
	render () {
		const editorClassName = className(
			'ReactCodeMirror',
			this.state.isFocused ? 'ReactCodeMirror--focused' : null,
			this.props.className
		);
		return (

				<textarea
					ref={ref => this.textareaNode = ref}
					name={this.props.name || this.props.path}
					defaultValue={this.props.value}
					autoComplete="off"
					autoFocus={this.props.autoFocus}
				/>

		);
	},
});

module.exports = CodeMirror;
<|endoftext|>


<commit_before>const React = require('react');
const className = require('classnames');
const debounce = require('lodash.debounce');

const CodeMirror = React.createClass({
	propTypes: {
		onChange: React.PropTypes.func,
		onFocusChange: React.PropTypes.func,
		options: React.PropTypes.object,
		path: React.PropTypes.string,
		value: React.PropTypes.string,
		className: React.PropTypes.any,
		codeMirrorInstance: React.PropTypes.object,
	},
	getCodeMirrorInstance() {
		return this.props.codeMirrorInstance || require('codemirror');
	},
	getInitialState () {
		return {
			isFocused: false,
		};
	},
	componentDidMount () {
		const textareaNode = this.refs.textarea;
		const codeMirrorInstance = this.getCodeMirrorInstance();
		this.codeMirror = codeMirrorInstance.fromTextArea(textareaNode, this.props.options);
		this.codeMirror.on('change', this.codemirrorValueChanged);
		this.codeMirror.on('focus', this.focusChanged.bind(this, true));
		this.codeMirror.on('blur', this.focusChanged.bind(this, false));
		this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
	},
	componentWillUnmount () {
		// is there a lighter-weight way to remove the cm instance?
		if (this.codeMirror) {
			this.codeMirror.toTextArea();
		}
	},
	componentWillReceiveProps: debounce(function (nextProps) {
		if (this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
			this.codeMirror.setValue(nextProps.value);
		}
		if (typeof nextProps.options === 'object') {
			for (let optionName in nextProps.options) {
				if (nextProps.options.hasOwnProperty(optionName)) {
					this.codeMirror.setOption(optionName, nextProps.options[optionName]);
				}
			}
		}
	}, 0),
	getCodeMirror () {
		return this.codeMirror;
	},
	focus () {
		if (this.codeMirror) {
			this.codeMirror.focus();
		}
	},
	focusChanged (focused) {
		this.setState({
			isFocused: focused,
		});
		this.props.onFocusChange && this.props.onFocusChange(focused);
	},
	codemirrorValueChanged (doc, change) {
		if (this.props.onChange && change.origin != 'setValue') {
			this.props.onChange(doc.getValue());
		}
	},
	render () {
		const editorClassName = className(
			'ReactCodeMirror',
			this.state.isFocused ? 'ReactCodeMirror--focused' : null,
			this.props.className
		);
		return (

				<textarea ref="textarea" name={this.props.path} defaultValue={this.props.value} autoComplete="off" />

		);
	},
});

module.exports = CodeMirror;
<commit_msg>adding onScroll event<commit_after>const React = require('react');
const className = require('classnames');
const debounce = require('lodash.debounce');

const CodeMirror = React.createClass({
	propTypes: {
		onChange: React.PropTypes.func,
		onFocusChange: React.PropTypes.func,
		onScroll: React.PropTypes.func,
		options: React.PropTypes.object,
		path: React.PropTypes.string,
		value: React.PropTypes.string,
		className: React.PropTypes.any,
		codeMirrorInstance: React.PropTypes.object,
	},
	getCodeMirrorInstance() {
		return this.props.codeMirrorInstance || require('codemirror');
	},
	getInitialState () {
		return {
			isFocused: false,
		};
	},
	componentDidMount () {
		const textareaNode = this.refs.textarea;
		const codeMirrorInstance = this.getCodeMirrorInstance();
		this.codeMirror = codeMirrorInstance.fromTextArea(textareaNode, this.props.options);
		this.codeMirror.on('change', this.codemirrorValueChanged);
		this.codeMirror.on('focus', this.focusChanged.bind(this, true));
		this.codeMirror.on('blur', this.focusChanged.bind(this, false));
		this.codeMirror.on('scroll', this.scrollChanged);
		this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
	},
	componentWillUnmount () {
		// is there a lighter-weight way to remove the cm instance?
		if (this.codeMirror) {
			this.codeMirror.toTextArea();
		}
	},
	componentWillReceiveProps: debounce(function (nextProps) {
		if (this.codeMirror && nextProps.value !== undefined && this.codeMirror.getValue() != nextProps.value) {
			this.codeMirror.setValue(nextProps.value);
		}
		if (typeof nextProps.options === 'object') {
			for (let optionName in nextProps.options) {
				if (nextProps.options.hasOwnProperty(optionName)) {
					this.codeMirror.setOption(optionName, nextProps.options[optionName]);
				}
			}
		}
	}, 0),
	getCodeMirror () {
		return this.codeMirror;
	},
	focus () {
		if (this.codeMirror) {
			this.codeMirror.focus();
		}
	},
	focusChanged (focused) {
		this.setState({
			isFocused: focused,
		});
		this.props.onFocusChange && this.props.onFocusChange(focused);
	},
	scrollChanged(cm) {
		this.props.onScroll && this.props.onScroll(cm.getScrollInfo());
	},
	codemirrorValueChanged (doc, change) {
		if (this.props.onChange && change.origin != 'setValue') {
			this.props.onChange(doc.getValue());
		}
	},
	render () {
		const editorClassName = className(
			'ReactCodeMirror',
			this.state.isFocused ? 'ReactCodeMirror--focused' : null,
			this.props.className
		);
		return (

				<textarea ref="textarea" name={this.props.path} defaultValue={this.props.value} autoComplete="off" />

		);
	},
});

module.exports = CodeMirror;
<|endoftext|>


<commit_before>var VirtualElement = require('famous-dom-renderers').VirtualElement;
var WebGLRenderer = require('famous-webgl-renderers').WebGLRenderer;

function Compositor() {
    this._contexts = {};
    this._outCommands = [];
    this._inCommands = [];
    this._renderers = [];
}

Compositor.CommandsToOutput = {
    CHANGE_TRANSFORM_ORIGIN: 'DOM',
    CHANGE_TRANSFORM: 'DOM',
    CHANGE_PROPERTY: 'DOM',
    CHANGE_CONTENT: 'DOM',
    ADD_EVENT_LISTENER: 'DOM',
    RECALL: 'DOM',
    GL_CREATE_MESH: 'GL',
    GL_UNIFORMS: 'GL',
    GL_BUFFER_DATA: 'GL',
    GL_SET_GEOMETRY: 'GL',
    GL_CREATE_LIGHT: 'GL',
    GL_LIGHT_POSITION: 'GL',
    GL_LIGHT_COLOR: 'GL'
};

Compositor.prototype.sendEvent = function sendEvent(path, ev, payload) {
    this._outCommands.push('WITH', path, 'TRIGGER', ev, payload);
};

Compositor.prototype.handleWith = function handleWith (commands) {
    var path = commands.shift();
    var pathArr = path.split('/');
    var context = this.getOrSetContext(pathArr.shift());
    var pointer = context;
    var index = pathArr.shift();
    var parent = context.DOM;
    while (pathArr.length) {
        if (!pointer[index]) pointer[index] = {};
        pointer = pointer[index];
        if (pointer.DOM) parent = pointer.DOM;
        index = pathArr.shift();
    }
    if (!pointer[index]) pointer[index] = {};
    pointer = pointer[index];
    var commandOutput = Compositor.CommandsToOutput[commands[0]];

    switch (commandOutput) {
        case 'DOM':
            var element = parent.getOrSetElement(path, index);
            element.receive(commands);
            pointer.DOM = element;
            break;

        case 'GL':
            if (!context.GL) {
                var webglrenderer = new WebGLRenderer(context.DOM);
                context.GL = webglrenderer;
                this._renderers.push(webglrenderer);
            }
            context.GL.receive(path, commands);
            break;
    }
};

Compositor.prototype.getOrSetContext = function getOrSetContext(selector) {
    if (this._contexts[selector]) return this._contexts[selector];
    var result = {
        DOM: new VirtualElement(document.querySelector(selector), selector, this)
    };
    result.DOM.setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    this._contexts[selector] = result;
    return result;
};

Compositor.prototype.giveSizeFor = function giveSizeFor(commands) {
    var selector = commands.shift();
    var size = this.getOrSetContext(selector).DOM._getSize();
    var report = {
        size: size
    };
    this._outCommands.push('WITH', selector, 'TRIGGER', 'RESIZE', report);
};

Compositor.prototype.drawCommands = function drawCommands() {
    var commands = this._inCommands;
    var command;

    while (commands.length) {
        command = commands.shift();
        switch (command) {
            case 'WITH':
                this.handleWith(commands);
                break;
            case 'NEED_SIZE_FOR':
                this.giveSizeFor(commands);
                break;
        }
    }

    for (var i = 0; i < this._renderers.length; i++) {
        this._renderers[i].draw();
    }

    return this._outCommands;
};

Compositor.prototype.receiveCommands = function receiveCommands(commands) {
    var len = commands.length;
    for (var i = 0; i < len; i++) {
        this._inCommands.push(commands[i]);
    }
};

Compositor.prototype.clearCommands = function clearCommands() {
    this._outCommands.length = 0;
};

module.exports = Compositor;
<commit_msg>fix: GL_CREATE_MESH command<commit_after>var VirtualElement = require('famous-dom-renderers').VirtualElement;
var WebGLRenderer = require('famous-webgl-renderers').WebGLRenderer;

function Compositor() {
    this._contexts = {};
    this._outCommands = [];
    this._inCommands = [];

    this._renderers = [];
}

Compositor.CommandsToOutput = {
    CHANGE_TRANSFORM_ORIGIN: 'DOM',
    CHANGE_TRANSFORM: 'DOM',
    CHANGE_PROPERTY: 'DOM',
    CHANGE_CONTENT: 'DOM',
    ADD_EVENT_LISTENER: 'DOM',
    RECALL: 'DOM',
    GL_UNIFORMS: 'GL',
    GL_BUFFER_DATA: 'GL',
    GL_SET_GEOMETRY: 'GL',
    GL_CREATE_LIGHT: 'GL',
    GL_LIGHT_POSITION: 'GL',
    GL_LIGHT_COLOR: 'GL',
    GL_CREATE_MESH: 'GL'
};

Compositor.prototype.sendEvent = function sendEvent(path, ev, payload) {
    this._outCommands.push('WITH', path, 'TRIGGER', ev, payload);
};

Compositor.prototype.handleWith = function handleWith (commands) {
    var path = commands.shift();
    var pathArr = path.split('/');
    var context = this.getOrSetContext(pathArr.shift());
    var pointer = context;
    var index = pathArr.shift();
    var parent = context.DOM;
    while (pathArr.length) {
        if (!pointer[index]) pointer[index] = {};
        pointer = pointer[index];
        if (pointer.DOM) parent = pointer.DOM;
        index = pathArr.shift();
    }
    if (!pointer[index]) pointer[index] = {};
    pointer = pointer[index];
    var commandOutput = Compositor.CommandsToOutput[commands[0]];

    switch (commandOutput) {
        case 'DOM':
            var element = parent.getOrSetElement(path, index);
            element.receive(commands);
            pointer.DOM = element;
            break;

        case 'GL':
            if (!context.GL) {
                var webglrenderer = new WebGLRenderer(context.DOM);
                context.GL = webglrenderer;
                this._renderers.push(webglrenderer);
            }
            context.GL.receive(path, commands);
            break;
    }
};

Compositor.prototype.getOrSetContext = function getOrSetContext(selector) {
    if (this._contexts[selector]) return this._contexts[selector];
    var result = {
        DOM: new VirtualElement(document.querySelector(selector), selector, this)
    };
    result.DOM.setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
    this._contexts[selector] = result;
    return result;
};

Compositor.prototype.giveSizeFor = function giveSizeFor(commands) {
    var selector = commands.shift();
    var size = this.getOrSetContext(selector).DOM._getSize();
    var report = {
        size: size
    };
    this._outCommands.push('WITH', selector, 'TRIGGER', 'resize', report);
};

Compositor.prototype.drawCommands = function drawCommands() {
    var commands = this._inCommands;
    var command;

    while (commands.length) {
        command = commands.shift();
        switch (command) {
            case 'WITH':
                this.handleWith(commands);
                break;
            case 'NEED_SIZE_FOR':
                this.giveSizeFor(commands);
                break;
        }
    }

    for (var i = 0; i < this._renderers.length; i++) {
        this._renderers[i].draw();
    }

    return this._outCommands;
};

Compositor.prototype.receiveCommands = function receiveCommands(commands) {
    var len = commands.length;
    for (var i = 0; i < len; i++) {
        this._inCommands.push(commands[i]);
    }
};

Compositor.prototype.clearCommands = function clearCommands() {
    this._outCommands.length = 0;
};

module.exports = Compositor;
<|endoftext|>


<commit_before>import colors from 'colors/safe';
import {errToString} from './util';
import moment from 'moment';
import net from 'net';
import tls from 'tls';
import protodef from 'rethinkdb/proto-def';
import url from 'url';

// A Connection instance wraps an incoming WebSocket (likely from a browser
// using rethinkdb-websocket-client) and an outgoing RethinkDB TCP socket. Once
// start() is called, it will forward traffic in both directions until either
// side disconnects. Each incoming WebSocket spawns a new outgoing TCP socket.
export class Connection {
  constructor(queryValidator, webSocket, loggingMode) {
    this.queryValidator = queryValidator;
    this.webSocket = webSocket;
    this.loggingMode = loggingMode;
    this.remoteAddress = webSocket._socket.remoteAddress;
    this.remotePort = webSocket._socket.remotePort;
  }

  start({sessionCreator, dbHost, dbPort, dbAuthKey, dbSsl}) {
    const urlQueryParams = url.parse(this.webSocket.upgradeReq.url, true).query;
    const req = this.webSocket.upgradeReq;
    this.sessionPromise = sessionCreator(urlQueryParams, req).catch(e => {
      this.cleanupAndLogErr('Error in sessionCreator', e);
    });
    this.dbAuthKey = dbAuthKey;
    this.wsInBuffer = new Buffer(0);
    this.handshakeComplete = false;
    this.isClosed = false;
    let options = {
      host: dbHost,
      port: dbPort
    };
    if (typeof dbSsl === 'boolean' && dbSsl) {
      this.dbSocket = tls.connect(options);
    } else if (typeof dbSsl === 'object') {
      options = {...dbSsl, ...options};
      this.dbSocket = tls.connect(options);
    } else {
      this.dbSocket = net.connect(options);
    }
    this.dbAuthKey = dbAuthKey;
    this.setupDbSocket();
    this.setupWebSocket();
    if (this.loggingMode === 'all') {
      this.log('Connect');
    }
  }

  setupDbSocket() {
    this.dbSocket.setNoDelay();
    this.dbSocket.on('data', data => {
      if (!this.isClosed) {
        try {
          if (this.webSocket.protocol === 'base64') {
            const b64EncodedData = (new Buffer(data)).toString('base64');
            this.webSocket.send(b64EncodedData);
          } else {
            this.webSocket.send(data, {binary: true});
          }
        } catch (e) {
          this.cleanupAndLogErr('Error recv dbSocket data', e);
        }
      }
    });
    this.dbSocket.on('end', () => {
      this.cleanup();
    });
    this.dbSocket.on('close', () => {
      if (this.loggingMode === 'all') {
        this.log('dbSocket closed');
      }
      this.cleanup();
    });
    this.dbSocket.on('error', e => {
      this.cleanupAndLogErr('dbSocket error', e);
    });
  }

  setupWebSocket() {
    this.webSocket.on('message', msg => {
      if (!this.isClosed) {
        try {
          this.handleWsMessage(msg);
        } catch (e) {
          this.cleanupAndLogErr('Error recv webSocket data', e);
        }
      }
    });
    this.webSocket.on('close', () => {
      if (this.loggingMode === 'all') {
        this.log('webSocket closed');
      }
      this.cleanup();
    });
    this.webSocket.on('error', e => {
      this.cleanupAndLogErr('webSocket error', e);
    });
  }

  log(msg, token) {
    const time = colors.blue(moment().format('HH:mm:ss.SSS'));
    const addr = colors.gray(this.remoteAddress + ':' + this.remotePort);
    const tokenStr = token ? colors.yellow(`tkn=${token} `) : '';
    console.log(`${time} ${addr} ${tokenStr}${msg}`);
  }

  cleanup() {
    this.isClosed = true;
    this.dbSocket.destroy();
    this.webSocket.close();
  }

  cleanupAndLogErr(msg, error) {
    this.cleanup();
    const fullMsg = error ? msg + '\n' + errToString(error) : msg;
    this.log(colors.red(fullMsg));
  }

  processClientCommand(cmdBuf) {
    // https://github.com/rethinkdb/rethinkdb/blob/61692c0ed4/drivers/javascript/net.coffee#L269-L273
    const token = cmdBuf.readUInt32LE(0) + 0x100000000 * cmdBuf.readUInt32LE(4);
    const queryCmdBuf = cmdBuf.slice(12, cmdBuf.length);
    const logFn = this.log.bind(this);
    const validatePromise = this.sessionPromise.then(session => (
      this.queryValidator.validateQueryCmd(token, queryCmdBuf, session, logFn)
    ));
    return validatePromise.then(allow => {
      if (allow) {
        this.dbSocket.write(cmdBuf, 'binary');
      }
      // TODO It might be nice to send a response back to the client of type
      // CLIENT_ERROR. However, we'd need to parse every message back from
      // the db in order to avoid injecting our response in the middle of a
      // large response.
      //
      // Right now, the query will be silently ignored in the frontend and
      // logged in the backend. This might lead to some sort of memory leak
      // in the frontend if it's waiting for responses from queries. This is
      // not very important, because properly written frontends should avoid
      // submitting queries that are denied.
      //
      // Alternatively, we could close the connection after a denied query,
      // as an easier to debug fail-fast option.
    });
  }

  validateClientHandshake(buf) {
    const protocolVersion = buf.readUInt32LE(0);
    if (protocolVersion !== protodef.VersionDummy.Version.V0_4) {
      this.cleanupAndLogErr('Invalid protocolVersion ' + protocolVersion);
      return 0;
    }
    const keyLength = buf.readUInt32LE(4);
    if (keyLength !== 0) {
      this.cleanupAndLogErr('Auth key not supported');
      return 0;
    }
    const protocolType = buf.readUInt32LE(8);
    if (protocolType !== protodef.VersionDummy.Protocol.JSON) {
      this.cleanupAndLogErr('Protocol type not supported ' + protocolType);
      return 0;
    }
    return 12;
  }

  processNextMessage(buf) {
    if (!this.handshakeComplete) {
      if (buf.length >= 12) {
        const clientHandshakeLength = this.validateClientHandshake(buf);
        if (clientHandshakeLength > 0) {
          const authKey = this.dbAuthKey || '';
          const outBuf = new Buffer(12 + authKey.length);
          outBuf.writeUInt32LE(protodef.VersionDummy.Version.V0_4, 0);
          outBuf.writeUInt32LE(authKey.length, 4);
          outBuf.write(authKey, 8);
          outBuf.writeUInt32LE(protodef.VersionDummy.Protocol.JSON, 8 + authKey.length);
          this.dbSocket.write(outBuf, 'binary');
          this.handshakeComplete = true;
          return clientHandshakeLength;
        } else {
          return 0;
        }
      }
    } else {
      if (buf.length >= 12) {
        const encodedQueryLength = buf.readUInt32LE(8);
        const queryEndOffset = 12 + encodedQueryLength;
        if (queryEndOffset <= buf.length) {
          const cmdBuf = buf.slice(0, queryEndOffset);
          this.processClientCommand(cmdBuf).catch(e => {
            this.cleanupAndLogErr('Error in processClientCommand', e);
          });
          return queryEndOffset;
        }
      }
    }
    return 0;
  }

  handleWsMessage(msg) {
    const isBase64 = typeof msg === 'string' && this.webSocket.protocol === 'base64';
    const incomingBuffer = isBase64 ? new Buffer(msg, 'base64') : msg;
    this.wsInBuffer = Buffer.concat([this.wsInBuffer, incomingBuffer]);
    let keepGoing = true;
    while (keepGoing) {
      let bytesConsumed = this.processNextMessage(this.wsInBuffer);
      this.wsInBuffer = this.wsInBuffer.slice(bytesConsumed);
      keepGoing = bytesConsumed > 0;
    }
  }
}
<commit_msg>Refactor by extracting sendWebSocketMessage method<commit_after>import colors from 'colors/safe';
import {errToString} from './util';
import moment from 'moment';
import net from 'net';
import tls from 'tls';
import protodef from 'rethinkdb/proto-def';
import url from 'url';

// A Connection instance wraps an incoming WebSocket (likely from a browser
// using rethinkdb-websocket-client) and an outgoing RethinkDB TCP socket. Once
// start() is called, it will forward traffic in both directions until either
// side disconnects. Each incoming WebSocket spawns a new outgoing TCP socket.
export class Connection {
  constructor(queryValidator, webSocket, loggingMode) {
    this.queryValidator = queryValidator;
    this.webSocket = webSocket;
    this.loggingMode = loggingMode;
    this.remoteAddress = webSocket._socket.remoteAddress;
    this.remotePort = webSocket._socket.remotePort;
  }

  start({sessionCreator, dbHost, dbPort, dbAuthKey, dbSsl}) {
    const urlQueryParams = url.parse(this.webSocket.upgradeReq.url, true).query;
    const req = this.webSocket.upgradeReq;
    this.sessionPromise = sessionCreator(urlQueryParams, req).catch(e => {
      this.cleanupAndLogErr('Error in sessionCreator', e);
    });
    this.dbAuthKey = dbAuthKey;
    this.wsInBuffer = new Buffer(0);
    this.handshakeComplete = false;
    this.isClosed = false;
    let options = {
      host: dbHost,
      port: dbPort
    };
    if (typeof dbSsl === 'boolean' && dbSsl) {
      this.dbSocket = tls.connect(options);
    } else if (typeof dbSsl === 'object') {
      options = {...dbSsl, ...options};
      this.dbSocket = tls.connect(options);
    } else {
      this.dbSocket = net.connect(options);
    }
    this.dbAuthKey = dbAuthKey;
    this.setupDbSocket();
    this.setupWebSocket();
    if (this.loggingMode === 'all') {
      this.log('Connect');
    }
  }

  sendWebSocketMessage(data) {
    if (this.webSocket.protocol === 'base64') {
      const b64EncodedData = (new Buffer(data)).toString('base64');
      this.webSocket.send(b64EncodedData);
    } else {
      this.webSocket.send(data, {binary: true});
    }
  }

  setupDbSocket() {
    this.dbSocket.setNoDelay();
    this.dbSocket.on('data', data => {
      if (!this.isClosed) {
        try {
          this.sendWebSocketMessage(data);
        } catch (e) {
          this.cleanupAndLogErr('Error recv dbSocket data', e);
        }
      }
    });
    this.dbSocket.on('end', () => {
      this.cleanup();
    });
    this.dbSocket.on('close', () => {
      if (this.loggingMode === 'all') {
        this.log('dbSocket closed');
      }
      this.cleanup();
    });
    this.dbSocket.on('error', e => {
      this.cleanupAndLogErr('dbSocket error', e);
    });
  }

  setupWebSocket() {
    this.webSocket.on('message', msg => {
      if (!this.isClosed) {
        try {
          this.handleWsMessage(msg);
        } catch (e) {
          this.cleanupAndLogErr('Error recv webSocket data', e);
        }
      }
    });
    this.webSocket.on('close', () => {
      if (this.loggingMode === 'all') {
        this.log('webSocket closed');
      }
      this.cleanup();
    });
    this.webSocket.on('error', e => {
      this.cleanupAndLogErr('webSocket error', e);
    });
  }

  log(msg, token) {
    const time = colors.blue(moment().format('HH:mm:ss.SSS'));
    const addr = colors.gray(this.remoteAddress + ':' + this.remotePort);
    const tokenStr = token ? colors.yellow(`tkn=${token} `) : '';
    console.log(`${time} ${addr} ${tokenStr}${msg}`);
  }

  cleanup() {
    this.isClosed = true;
    this.dbSocket.destroy();
    this.webSocket.close();
  }

  cleanupAndLogErr(msg, error) {
    this.cleanup();
    const fullMsg = error ? msg + '\n' + errToString(error) : msg;
    this.log(colors.red(fullMsg));
  }

  processClientCommand(cmdBuf) {
    // https://github.com/rethinkdb/rethinkdb/blob/61692c0ed4/drivers/javascript/net.coffee#L269-L273
    const token = cmdBuf.readUInt32LE(0) + 0x100000000 * cmdBuf.readUInt32LE(4);
    const queryCmdBuf = cmdBuf.slice(12, cmdBuf.length);
    const logFn = this.log.bind(this);
    const validatePromise = this.sessionPromise.then(session => (
      this.queryValidator.validateQueryCmd(token, queryCmdBuf, session, logFn)
    ));
    return validatePromise.then(allow => {
      if (allow) {
        this.dbSocket.write(cmdBuf, 'binary');
      }
      // TODO It might be nice to send a response back to the client of type
      // CLIENT_ERROR. However, we'd need to parse every message back from
      // the db in order to avoid injecting our response in the middle of a
      // large response.
      //
      // Right now, the query will be silently ignored in the frontend and
      // logged in the backend. This might lead to some sort of memory leak
      // in the frontend if it's waiting for responses from queries. This is
      // not very important, because properly written frontends should avoid
      // submitting queries that are denied.
      //
      // Alternatively, we could close the connection after a denied query,
      // as an easier to debug fail-fast option.
    });
  }

  validateClientHandshake(buf) {
    const protocolVersion = buf.readUInt32LE(0);
    if (protocolVersion !== protodef.VersionDummy.Version.V0_4) {
      this.cleanupAndLogErr('Invalid protocolVersion ' + protocolVersion);
      return 0;
    }
    const keyLength = buf.readUInt32LE(4);
    if (keyLength !== 0) {
      this.cleanupAndLogErr('Auth key not supported');
      return 0;
    }
    const protocolType = buf.readUInt32LE(8);
    if (protocolType !== protodef.VersionDummy.Protocol.JSON) {
      this.cleanupAndLogErr('Protocol type not supported ' + protocolType);
      return 0;
    }
    return 12;
  }

  processNextMessage(buf) {
    if (!this.handshakeComplete) {
      if (buf.length >= 12) {
        const clientHandshakeLength = this.validateClientHandshake(buf);
        if (clientHandshakeLength > 0) {
          const authKey = this.dbAuthKey || '';
          const outBuf = new Buffer(12 + authKey.length);
          outBuf.writeUInt32LE(protodef.VersionDummy.Version.V0_4, 0);
          outBuf.writeUInt32LE(authKey.length, 4);
          outBuf.write(authKey, 8);
          outBuf.writeUInt32LE(protodef.VersionDummy.Protocol.JSON, 8 + authKey.length);
          this.dbSocket.write(outBuf, 'binary');
          this.handshakeComplete = true;
          return clientHandshakeLength;
        } else {
          return 0;
        }
      }
    } else {
      if (buf.length >= 12) {
        const encodedQueryLength = buf.readUInt32LE(8);
        const queryEndOffset = 12 + encodedQueryLength;
        if (queryEndOffset <= buf.length) {
          const cmdBuf = buf.slice(0, queryEndOffset);
          this.processClientCommand(cmdBuf).catch(e => {
            this.cleanupAndLogErr('Error in processClientCommand', e);
          });
          return queryEndOffset;
        }
      }
    }
    return 0;
  }

  handleWsMessage(msg) {
    const isBase64 = typeof msg === 'string' && this.webSocket.protocol === 'base64';
    const incomingBuffer = isBase64 ? new Buffer(msg, 'base64') : msg;
    this.wsInBuffer = Buffer.concat([this.wsInBuffer, incomingBuffer]);
    let keepGoing = true;
    while (keepGoing) {
      let bytesConsumed = this.processNextMessage(this.wsInBuffer);
      this.wsInBuffer = this.wsInBuffer.slice(bytesConsumed);
      keepGoing = bytesConsumed > 0;
    }
  }
}
<|endoftext|>