<commit_before>// 69: Reflect - defineProperty
// To do: make all tests pass, leave the assert lines unchanged!

describe('`Reflect.defineProperty()` is like `Object.defineProperty()` but returns a Boolean.', function() {

  describe('the function itself', function() {
    it('is static on the `Reflect` object', function() {
      const name = 'defineProperty';
      assert.equal(name in Reflect, true);
    });
    it('is of type `function`', function() {
      const expectedType = 'function';
      assert.equal(typeof Reflect.defineProperty, expectedType)
    });
  });

  describe('the 1st parameter is the object on which to define a property', function() {
    it('fails if it is not an object', function() {
      assert.throws(() => { Reflect.defineProperty('not an object'); }, TypeError);
    });
    it('accepts an object', function() {
      assert.doesNotThrow(() => { Reflect.defineProperty({}); });
    });
    it('accepts an instance (of a class)', function() {
      let instance = new class {};
      assert.doesNotThrow(() => { Reflect.defineProperty(instance); });
    });
  });

  describe('2nd parameter', function() {
    describe('when it is/becomes a string', function() {
      it('works with a `normal` string', function() {
        let obj = {};
        Reflect.defineProperty(obj, 'prop', {});
        assert.equal('prop' in obj, true);
      });
      it('a number gets converted into a string', function() {
        let obj = {};
        Reflect.defineProperty(obj, 1, {});
        assert.equal('1' in obj, true);
      });
      it('`undefined` also gets converted into a string (watch out!)', function() {
        let obj = {};
        let undef;
        Reflect.defineProperty(obj, undef, {});
        assert.equal('undefined' in obj, true);
      });
    });
    describe('not a string', function() {
      it('it can be a symbol', function() {
        let obj = {};
        const sym = Symbol.for('prop');
        Reflect.defineProperty(obj, sym, {});
        assert.equal(sym in obj, true);
      });
    });
  });

  describe('the `value` in the 3rd parameter', function() {
    // The complexity of the 3rd parameter might be covered in another kata.
    it('is the initial value of the property', function() {
      let obj = {};
      Reflect.defineProperty(obj, 'prop', {value: 'property value'});
      assert.equal(obj.prop, 'property value');
    });
    it('can be of any type', function() {
      let obj = {};
      Reflect.defineProperty(obj, 'prop', {value: obj});
      assert.equal(obj.prop, obj);
    });
  });

  describe('return value', function() {
    describe('returns true', function() {
      it('when the property was created', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance, 1, {});
        assert.equal(wasPropertyDefined, true);
      });
      it('no matter what the value of the property is', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance, 'prop', {});
        assert.equal(wasPropertyDefined, true);
      });
    });
    describe('returns false', function() {
      it('when no property name is given (since no property has been added)', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance);
        assert.equal(wasPropertyDefined, false);
      });
      it('when `undefined` is given as a property name', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance, void 0);
        assert.equal(wasPropertyDefined, false);
      });
      it('when no 3rd parameter, the descriptor is given', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance, 'prop');
        assert.equal(wasPropertyDefined, false);
      });
    });
  });

});<commit_msg>Make it a kata and remove wrong test.<commit_after>// 69: Reflect - defineProperty
// To do: make all tests pass, leave the assert lines unchanged!

describe('`Reflect.defineProperty()` is like `Object.defineProperty()` but returns a Boolean.', function() {

  describe('the function itself', function() {
    it('is static on the `Reflect` object', function() {
      const name = 'waht`s the functions name again? :)';
      assert.equal(name in Reflect, true);
    });
    it('is of type `function`', function() {
      const expectedType = '';
      assert.equal(typeof Reflect.defineProperty, expectedType)
    });
  });

  describe('the 1st parameter is the object on which to define a property', function() {
    it('fails if it is not an object', function() {
      let noObj = {};
      assert.throws(() => { Reflect.defineProperty(noObj); }, TypeError);
    });
    it('accepts an object', function() {
      let obj = '';
      assert.doesNotThrow(() => { Reflect.defineProperty(obj); });
    });
    it('accepts an instance (of a class)', function() {
      let instance;
      assert.doesNotThrow(() => { Reflect.defineProperty(instance); });
    });
  });

  describe('2nd parameter', function() {
    describe('when it is/becomes a string', function() {
      it('works with a `normal` string', function() {
        let obj = {};
        Reflect.defineProperty(obj, '', {});
        assert.equal('prop' in obj, true);
      });
      it('a number gets converted into a string', function() {
        let obj = {};
        Reflect.defineProperty(obj, 2, {});
        assert.equal('1' in obj, true);
      });
      it('`undefined` also gets converted into a string (watch out!)', function() {
        let obj = {};
        let undef = 1;
        Reflect.defineProperty(obj, undef, {});
        assert.equal('undefined' in obj, true);
      });
    });
    describe('not a string', function() {
      it('it can be a symbol', function() {
        let obj = {};
        const sym = Symbol.for('prop');
        Reflect.defineProperty(obj, 'prop', {});
        assert.equal(sym in obj, true);
      });
    });
  });

  describe('the `value` in the 3rd parameter', function() {
    // The complexity of the 3rd parameter might be covered in later kata.
    it('contains the initial value of the property, as an object in the property `value`', function() {
      let obj = {};
      Reflect.defineProperty(obj, 'prop');
      assert.equal(obj.prop, 'property value');
    });
    it('can be of any type (even itself)', function() {
      let obj = {};
      Reflect.defineProperty(obj, 'prop');
      assert.equal(obj.prop, obj);
    });
  });

  describe('return value', function() {
    describe('returns true', function() {
      it('when the property was created (which requires the 3rd parameter too!!!)', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty();
        assert.equal(wasPropertyDefined, true);
      });
      it('no matter what the value of the property is (just the 3rd param has to exist as `{}`)', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance);
        assert.equal(wasPropertyDefined, true);
      });
    });
    describe('returns false', function() {
      it('when no property name is given (since no property has been added)', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty;
        assert.equal(wasPropertyDefined, false);
      });
      it('when no 3rd parameter, the descriptor is given', function() {
        let instance = new class {};
        const wasPropertyDefined = Reflect.defineProperty(instance, 'prop', {value: 1});
        assert.equal(wasPropertyDefined, false);
      });
    });
  });

});<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014-2014 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var TableController = {
    isVisible: false,
    pageStart: 0,
    pageSize: Settings.pagesize || 10,
    init: function() {
        this.tableButton = $('[data-action="dataTable"]');
        this.tableView = $('#tableView');
        this.tableButton.show();
        this.tableButton.on('click', $.proxy(function(event) {
            var button = $(event.currentTarget);
            var legendButton = $('[data-toggle="legend"]');
            if (this.isVisible === false) {
                this.isVisible = true;
                this.tableView.show();
                Button.setNewIcon(button, 'glyphicon-stats');
                legendButton.hide();
                EventManager.publish("navigation:open", "table");
            } else {
                this.isVisible = false;
                this.tableView.hide();
                legendButton.show();
                Button.removeNewIcon(button, 'glyphicon-stats');
                EventManager.publish("navigation:close", "table");
            }
        }, this));
        EventManager.subscribe("navigation:open", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:synced", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:remove", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:changeStyle", $.proxy(this.updateTable, this));
    },
    createTable: function() {
        if (this.isVisible) {
            this.tableView.empty();
            this.sortingFunc = null;
            this.pageStart = 0;
            this.createHtmlTableHeader();
            this.tableView.append(this.htmltable);
            this.updateTable();
            this.createHeaderClickHandler();
        }
    },
    updateTable: function () {
        if (this.isVisible) {
            var array = this.createValueArray();
            if (!this.sortingFunc) {
                this.sortingFunc = this.upsort(0);
            }
            array.sort(this.sortingFunc);
            if (array.length > 0) {
                var colorArray = this.createColorArray();
                this.createHtmlTable(array, colorArray);
            }
            this.createPaging(array.length, this.pageSize, this.pageStart);
            this.createPagingClickHandler(array.length);
        }
    },
    createHeaderClickHandler: function() {
        $('.table th').on('click', $.proxy(function(event) {
            var target = $(event.currentTarget);
            var index = target.data('index');
            if (target.hasClass('sortedUp')) {
                target.removeClass('sortedUp').addClass('sortedDown');
                this.changeSortLabel(target, false);
                this.sortingFunc = this.downsort(index);
            } else if (target.hasClass('sortedDown')) {
                target.removeClass('sortedDown').addClass('sortedUp');
                this.changeSortLabel(target, true);
                this.sortingFunc = this.upsort(index);
            } else {
                $('.table th').removeClass('sortedUp').removeClass('sortedDown');
                target.addClass('sortedUp');
                this.changeSortLabel(target, true);
                this.sortingFunc = this.upsort(index);
            }
            this.updateTable();
        }, this));
    },
    changeSortLabel: function(target, up) {
        $('#sorting').remove();
        var span = $('').attr('id', 'sorting');
        if (up) {
            span.html(' ▾');
        } else {
            span.html(' ▴');
        }
        target.append(span);
    },
    createPagingClickHandler: function(length) {
        $('.pagination li a').on('click', $.proxy(function(event) {
            var start = $(event.target).data('start');
            if (typeof (start) !== "undefined" && start >= 0 && start <= length) {
                this.pageStart = start;
                this.updateTable();
            }
        }, this));
    },
    createPaging: function(arraylength, pagesize, pagestart) {
        this.tableView.find('div.paging').remove();
        var div = $('

'),
                paging = $('

');
        paging.append(this.pageButton('«', 0));
        paging.append(this.pageButton('‹', pagestart - pagesize));
        paging.append(this.pageButton(Math.ceil((pagestart + 1) / pagesize) + '/' + Math.ceil(arraylength / pagesize)));
        paging.append(this.pageButton('›', pagestart + pagesize));
        paging.append(this.pageButton('»', Math.floor(arraylength / pagesize) * pagesize));
        div.append(paging);
        this.tableView.append(div);
    },
    pageButton: function(label, start) {
        var elem = $('

'),
                a = $('' + label + '');
        a.data('start', start);
        elem.append(a);
        return elem;
    },
    createColorArray: function() {
        var array = [];
        $.each(TimeSeriesController.getTimeseriesCollection(), function(index, ts) {
            array.push(ts.getStyle().getColor());
        });
        return array;
    },
    createValueArray: function() {
        var array = [];
        var map = {};
        var tscount = Object.keys(TimeSeriesController.getTimeseriesCollection()).length; // TODO count setzen
        var count = 0;
        $.each(TimeSeriesController.getTimeseriesCollection(), $.proxy(function(index, ts) {
            var values = Time.removeOverlappingValues(ts.getValues());
            $.each(values, $.proxy(function(valueIdx, pair){
                var time = pair[0];
                var value = pair[1];
                if (!map[time]) {
                    map[time] = new Array(tscount);
                }
                map[time][count] = value;
            }, this));
            count++;
        }, this));
        var i = 0;
        Object.keys(map).map(function(value) {
            var temp = [];
            temp[0] = parseInt(value);
            $.each(map[value], $.proxy(function(idx, value){
                temp[idx+1] = value;
            },this));
            array[i++] = temp;
        });
        return array;
    },
    upsort: function(id) {
        return function(a, b) {
            if (isNaN(a[id]) && isNaN(b[id]))
                return 0;
            if (isNaN(a[id]))
                return -1;
            if (isNaN(b[id]))
                return 1;
            return a[id] - b[id];
        };
    },
    downsort: function(id) {
        return function(a, b) {
            if (isNaN(a[id]) && isNaN(b[id]))
                return 0;
            if (isNaN(a[id]))
                return 1;
            if (isNaN(b[id]))
                return -1;
            return b[id] - a[id];
        };
    },
    createHtmlTableHeader: function() {
        this.htmltable = $('

').addClass('table').addClass('table-condensed');
        var header = $('

');
        var headerrow = $('

');
        headerrow.append($('	').data('index', 0).text(_('table.time')));
        var index = 1;
        $.each(TimeSeriesController.getTimeseriesCollection(), function(id, elem) {
            var title = $('

').text(elem.getStationLabel());
            var phenomenonLabel = $('').text(elem.getPhenomenonLabel() + " (" + elem.getUom() + ")");
            var categoryLabel = $('

').text(elem.getCategoryLabel());
            headerrow.append($('	').data('index', index++).append(title).append(phenomenonLabel).append(categoryLabel));
        });
        this.htmltable.append(header.append(headerrow));
    },
    createHtmlTable: function(array, colorArray) {
        this.htmltable.find('tbody tr').remove();
        var cArray = colorArray;
        $.each(array, $.proxy(function(tsIndex, elem) {
            if (tsIndex < this.pageStart || tsIndex >= (this.pageStart + this.pageSize)) {
                return;
            }
            var row = $('

');
            $.each(elem, function(index, value) {
                if (index === 0) {
                    row.append($('	').text(
                            moment(value).format(Settings.dateformat)));
                } else {
                    row.append($('	').css('color', cArray[index - 1])
                            .append($('').text(value)));
                }
            });
            this.htmltable.append(row);
        }, this));
        return this.htmltable;
    }
};<commit_msg>fix issue in tablecontroller for timeseries with no values in selected timespan<commit_after>/*
 * Copyright (C) 2014-2014 52°North Initiative for Geospatial Open Source
 * Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var TableController = {
    isVisible: false,
    pageStart: 0,
    pageSize: Settings.pagesize || 10,
    init: function () {
        this.tableButton = $('[data-action="dataTable"]');
        this.tableView = $('#tableView');
        this.tableButton.show();
        this.tableButton.on('click', $.proxy(function (event) {
            var button = $(event.currentTarget);
            var legendButton = $('[data-toggle="legend"]');
            if (this.isVisible === false) {
                this.isVisible = true;
                this.tableView.show();
                Button.setNewIcon(button, 'glyphicon-stats');
                legendButton.hide();
                EventManager.publish("navigation:open", "table");
            } else {
                this.isVisible = false;
                this.tableView.hide();
                legendButton.show();
                Button.removeNewIcon(button, 'glyphicon-stats');
                EventManager.publish("navigation:close", "table");
            }
        }, this));
        EventManager.subscribe("navigation:open", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:synced", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:remove", $.proxy(this.createTable, this));
        EventManager.subscribe("timeseries:changeStyle", $.proxy(this.updateTable, this));
    },
    createTable: function () {
        if (this.isVisible) {
            this.tableView.empty();
            this.sortingFunc = null;
            this.pageStart = 0;
            this.createHtmlTableHeader();
            this.tableView.append(this.htmltable);
            this.updateTable();
            this.createHeaderClickHandler();
        }
    },
    updateTable: function () {
        if (this.isVisible) {
            var array = this.createValueArray();
            if (!this.sortingFunc) {
                this.sortingFunc = this.upsort(0);
            }
            array.sort(this.sortingFunc);
            if (array.length > 0) {
                var colorArray = this.createColorArray();
                this.createHtmlTable(array, colorArray);
            }
            this.createPaging(array.length, this.pageSize, this.pageStart);
            this.createPagingClickHandler(array.length);
        }
    },
    createHeaderClickHandler: function () {
        $('.table th').on('click', $.proxy(function (event) {
            var target = $(event.currentTarget);
            var index = target.data('index');
            if (target.hasClass('sortedUp')) {
                target.removeClass('sortedUp').addClass('sortedDown');
                this.changeSortLabel(target, false);
                this.sortingFunc = this.downsort(index);
            } else if (target.hasClass('sortedDown')) {
                target.removeClass('sortedDown').addClass('sortedUp');
                this.changeSortLabel(target, true);
                this.sortingFunc = this.upsort(index);
            } else {
                $('.table th').removeClass('sortedUp').removeClass('sortedDown');
                target.addClass('sortedUp');
                this.changeSortLabel(target, true);
                this.sortingFunc = this.upsort(index);
            }
            this.updateTable();
        }, this));
    },
    changeSortLabel: function (target, up) {
        $('#sorting').remove();
        var span = $('').attr('id', 'sorting');
        if (up) {
            span.html(' ▾');
        } else {
            span.html(' ▴');
        }
        target.append(span);
    },
    createPagingClickHandler: function (length) {
        $('.pagination li a').on('click', $.proxy(function (event) {
            var start = $(event.target).data('start');
            if (typeof (start) !== "undefined" && start >= 0 && start <= length) {
                this.pageStart = start;
                this.updateTable();
            }
        }, this));
    },
    createPaging: function (arraylength, pagesize, pagestart) {
        this.tableView.find('div.paging').remove();
        var div = $('

'),
                paging = $('

');
        paging.append(this.pageButton('«', 0));
        paging.append(this.pageButton('‹', pagestart - pagesize));
        paging.append(this.pageButton(Math.ceil((pagestart + 1) / pagesize) + '/' + Math.ceil(arraylength / pagesize)));
        paging.append(this.pageButton('›', pagestart + pagesize));
        paging.append(this.pageButton('»', Math.floor(arraylength / pagesize) * pagesize));
        div.append(paging);
        this.tableView.append(div);
    },
    pageButton: function (label, start) {
        var elem = $('

'),
                a = $('' + label + '');
        a.data('start', start);
        elem.append(a);
        return elem;
    },
    createColorArray: function () {
        var array = [];
        $.each(TimeSeriesController.getTimeseriesCollection(), function (index, ts) {
            array.push(ts.getStyle().getColor());
        });
        return array;
    },
    createValueArray: function () {
        var array = [];
        var map = {};
        var tscount = Object.keys(TimeSeriesController.getTimeseriesCollection()).length; // TODO count setzen
        var count = 0;
        $.each(TimeSeriesController.getTimeseriesCollection(), $.proxy(function (index, ts) {
            if (ts.getValues().length > 0) {
                var values = Time.removeOverlappingValues(ts.getValues());
                $.each(values, $.proxy(function (valueIdx, pair) {
                    var time = pair[0];
                    var value = pair[1];
                    if (!map[time]) {
                        map[time] = new Array(tscount);
                    }
                    map[time][count] = value;
                }, this));
            }
            count++;
        }, this));
        var i = 0;
        Object.keys(map).map(function (value) {
            var temp = [];
            temp[0] = parseInt(value);
            $.each(map[value], $.proxy(function (idx, value) {
                temp[idx + 1] = value;
            }, this));
            array[i++] = temp;
        });
        return array;
    },
    upsort: function (id) {
        return function (a, b) {
            if (isNaN(a[id]) && isNaN(b[id]))
                return 0;
            if (isNaN(a[id]))
                return -1;
            if (isNaN(b[id]))
                return 1;
            return a[id] - b[id];
        };
    },
    downsort: function (id) {
        return function (a, b) {
            if (isNaN(a[id]) && isNaN(b[id]))
                return 0;
            if (isNaN(a[id]))
                return 1;
            if (isNaN(b[id]))
                return -1;
            return b[id] - a[id];
        };
    },
    createHtmlTableHeader: function () {
        this.htmltable = $('

').addClass('table').addClass('table-condensed');
        var header = $('

');
        var headerrow = $('

');
        headerrow.append($('	').data('index', 0).text(_('table.time')));
        var index = 1;
        $.each(TimeSeriesController.getTimeseriesCollection(), function (id, elem) {
            var title = $('

').text(elem.getStationLabel());
            var phenomenonLabel = $('').text(elem.getPhenomenonLabel() + " (" + elem.getUom() + ")");
            var categoryLabel = $('

').text(elem.getCategoryLabel());
            headerrow.append($('	').data('index', index++).append(title).append(phenomenonLabel).append(categoryLabel));
        });
        this.htmltable.append(header.append(headerrow));
    },
    createHtmlTable: function (array, colorArray) {
        this.htmltable.find('tbody tr').remove();
        var cArray = colorArray;
        $.each(array, $.proxy(function (tsIndex, elem) {
            if (tsIndex < this.pageStart || tsIndex >= (this.pageStart + this.pageSize)) {
                return;
            }
            var row = $('

');
            $.each(elem, function (index, value) {
                if (index === 0) {
                    row.append($('	').text(
                            moment(value).format(Settings.dateformat)));
                } else {
                    row.append($('	').css('color', cArray[index - 1])
                            .append($('').text(value)));
                }
            });
            this.htmltable.append(row);
        }, this));
        return this.htmltable;
    }
};<|endoftext|>


<commit_before>svgEditor.readLang({
	lang: "fr",
	dir : "ltr",
	common: {
		"ok": "OK",
		"cancel": "Annuler",
		"key_backspace": "Suppr.",
		"key_del": "Retour Arr.",
		"key_down": "Bas",
		"key_up": "Haut",
		"more_opts": "Plus d'options",
		"url": "URL",
		"width": "Largeur",
		"height": "Hauteur"
	},
	misc: {
		"powered_by": "Powered by"
	},
	ui: {
		"toggle_stroke_tools": "Montrer/Cacher plus d'outils de Contour",
		"palette_info": "Cliquer pour changer la couleur de remplissage, Shift-Clic pour changer la couleur de contour",
		"zoom_level": "Changer le niveau de zoom",
		"panel_drag": "Tirer vers la gauche/droite pour redimentionner le panneau"
	},
	properties: {
		"id": "Identifier l'élément",
		"fill_color": "Changer la couleur de remplissage",
		"stroke_color": "Changer la couleur du contour",
		"stroke_style": "Changer le style du contour",
		"stroke_width": "Changer la largeur du contour de 1, Shift-Click pour changer la largeur de 0.1",
		"pos_x": "Changer la position horizontale X",
		"pos_y": "Changer la position verticale Y",
		"linecap_butt": "Terminaison : Sur le nœud",
		"linecap_round": "Terminaison : Arrondie",
		"linecap_square": "Terminaison : Carrée",
		"linejoin_bevel": "Raccord : Biseauté",
		"linejoin_miter": "Raccord : Droit",
		"linejoin_round": "Raccord : Arrondi",
		"angle": "Changer l'angle de rotation",
		"blur": "Changer la valeur du flou gaussien",
		"opacity": "Changer l'opacité de l'élément sélectionné",
		"circle_cx": "Changer la position horizontale cx du cercle",
		"circle_cy": "Changer la position verticale cy du cercle",
		"circle_r": "Changer le rayon du cercle",
		"ellipse_cx": "Changer la position horizontale cx de l'ellipse",
		"ellipse_cy": "Changer la position verticale cy de l'ellipse",
		"ellipse_rx": "Changer le rayon horizontal x de l'ellipse",
		"ellipse_ry": "Changer le rayon vertical y de l'ellipse",
		"line_x1": "Changer la position horizontale x de début de la ligne",
		"line_x2": "Changer la position horizontale x de fin de la ligne",
		"line_y1": "Changer la position verticale y de début de la ligne",
		"line_y2": "Changer la position verticale y de fin de la ligne",
		"rect_height": "Changer la hauteur du rectangle",
		"rect_width": "Changer la largeur du rectangle",
		"corner_radius": "Changer le rayon des coins du rectangle",
		"image_width": "Changer la largeur de l'image",
		"image_height": "Changer la hauteur de l'image",
		"image_url": "Modifier l'URL",
		"node_x": "Changer la positon horizontale x du nœud",
		"node_y": "Changer la position verticale y du nœud",
		"seg_type": "Changer le type du Segment",
		"straight_segments": "Droit",
		"curve_segments": "Courbe",
		"text_contents": "Changer le contenu du texte",
		"font_family": "Changer la famille de police",
		"font_size": "Changer la taille de la police",
		"bold": "Texte en gras",
		"italic": "Texte en italique"
	},
	tools: {
		"main_menu": "Menu principal",
		"bkgnd_color_opac": "Changer la couleur d'arrière-plan / l'opacité",
		"connector_no_arrow": "Sans flèches",
		"fitToContent": "Ajuster au contenu",
		"fit_to_all": "Ajuster au contenu de tous les calques",
		"fit_to_canvas": "Ajuster au canevas",
		"fit_to_layer_content": "Ajuster au contenu du calque",
		"fit_to_sel": "Ajuster à la sélection",
		"align_relative_to": "Aligner par rapport à ...",
		"relativeTo": "Relativement à:",
		"Page": "Page",
		"largest_object": "Plus gros objet",
		"selected_objects": "Objets sélectionnés",
		"smallest_object": "Plus petit objet",
		"new_doc": "Nouvelle image",
		"open_doc": "Ouvrir une image",
		"export_png": "Exporter au format PNG",
		"save_doc": "Enregistrer l'image",
		"import_doc": "Importer un objet SVG",
		"align_to_page": "Aligner l'élément relativement à la Page",
		"align_bottom": "Aligner le bas des objets",
		"align_center": "Centrer verticalement",
		"align_left": "Aligner les côtés gauches",
		"align_middle": "Centrer horizontalement",
		"align_right": "Aligner les côtés droits",
		"align_top": "Aligner le haut des objets",
		"mode_select": "Outil de sélection",
		"mode_fhpath": "Crayon à main levée",
		"mode_line": "Tracer des lignes",
		"mode_connect": "Connecter deux objets",
		"mode_rect": "Outil rectangle",
		"mode_square": "Outils carré",
		"mode_fhrect": "Rectangle main levée",
		"mode_ellipse": "Ellipse",
		"mode_circle": "Cercle",
		"mode_fhellipse": "Ellipse main levée",
		"mode_path": "Outil Chemin",
		"mode_shapelib": "Bibliothèque d'images",
		"mode_text": "Outil Texte",
		"mode_image": "Outil Image",
		"mode_zoom": "Zoom",
		"mode_eyedropper": "Outil Pipette",
		"no_embed": "NOTE: Cette image ne peut être incorporée en tant que données. Le contenu affiché sera celui de l'image située à cette adresse",
		"undo": "Annuler l'action",
		"redo": "Refaire l'action",
		"tool_source": "Modifier la source",
		"wireframe_mode": "Mode Fil de Fer",
		"toggle_grid": "Montrer/cacher la grille",
		"clone": "Cloner élement(s)",
		"del": "Supprimer élement(s)",
		"group": "Grouper les éléments",
		"make_link": "Créer (hyper)lien",
		"set_link_url": "Set link URL (leave empty to remove)",
		"to_path": "Convertir en chemin",
		"reorient_path": "Réorienter le chemin",
		"ungroup": "Dégrouper les éléments",
		"docprops": "Propriétés du document",
		"imagelib": "Bibliothèque d'images",
		"move_bottom": "Déplacer vers le bas",
		"move_top": "Déplacer vers le haut",
		"node_clone": "Cloner le nœud",
		"node_delete": "Supprimer le nœud",
		"node_link": "Rendre les points de contrôle solidaires",
		"add_subpath": "Ajouter un sous-chemin",
		"openclose_path": "Ouvrir/Fermer sous-chemin",
		"source_save": "Appliquer Modifications",
		"cut": "Couper",
		"copy": "Copier",
		"paste": "Coller",
		"paste_in_place": "Coller sur place",
		"Retour Arr.": "Supprimer",
		"move_front": "Placer au premier plan",
		"move_up": "Placer plus près",
		"move_down": "Placer au dernier plan",
		"move_back": "Placer plus au fond"
	},
	layers: {
		"layer":"Calque",
		"layers": "Calques",
		"del": "Supprimer le calque",
		"move_down": "Descendre le calque",
		"new": "Nouveau calque",
		"rename": "Renommer le calque",
		"move_up": "Monter le calque",
		"dupe": "Dupliquer calque",
		"merge_down": "Fusionner vers le bas",
		"merge_all": "Tout fusionner",
		"move_elems_to": "Déplacer éléments vers:",
		"move_selected": "Déplacer les éléments sélectionnés vers un autre calque"
	},
	config: {
		"image_props": "Propriétés de l'Image",
		"doc_title": "Titre",
		"doc_dims": "Dimensions du canevas",
		"included_images": "Images incorporées",
		"image_opt_embed": "Incorporer les images en tant que données (fichiers locaux)",
		"image_opt_ref": "Utiliser la référence des images ",
		"editor_prefs": "Préférences de l'Éditeur",
		"icon_size": "Taille des icônes",
		"language": "Langue",
		"background": "Toile de fond de l'Éditeur",
		"editor_img_url": "Image URL",
		"editor_bg_note": "Note: La toile de fond n'est pas sauvegardée avec l'image.",
		"icon_large": "Grande",
		"icon_medium": "Moyenne",
		"icon_small": "Petite",
		"icon_xlarge": "Super-Grande",
		"select_predefined": "Sélectionner prédéfinis:",
		"units_and_rulers": "Unités & Règles",
		"show_rulers": "Afficher les règles",
		"base_unit": "Unité de mesure:",
		"grid": "Grille",
		"snapping_onoff": "Snapping on/off",
		"snapping_stepsize": "Snapping Step-Size:",
		"grid_color": "Couleur de la grille"
	},
	shape_cats: {
		"basic": "Basique",
		"object": "Objets",
		"symbol": "Symboles",
		"arrow": "Flêches",
		"flowchart": "Diagramme de flux",
		"animal": "Animaux",
		"game": "Cartes & Echecs",
		"dialog_balloon": "Bulles de dialogue",
		"electronics": "Electronique",
		"math": "Mathématiques",
		"music": "Musique",
		"misc": "Divers",
		"raphael_1": "raphaeljs.com ensemble 1",
		"raphael_2": "raphaeljs.com ensemble 2"
	},
	imagelib: {
		"select_lib": "Select an image library",
		"show_list": "Show library list",
		"import_single": "Import single",
		"import_multi": "Import multiple",
		"open": "Open as new document"
	},
	notification: {
		"invalidAttrValGiven":"Valeur fournie invalide",
		"noContentToFitTo":"Il n'y a pas de contenu auquel ajuster",
		"dupeLayerName":"Il existe déjà un calque de ce nom !",
		"enterUniqueLayerName":"Veuillez entrer un nom (unique) pour le calque",
		"enterNewLayerName":"Veuillez entrer le nouveau nom du calque",
		"layerHasThatName":"Le calque porte déjà ce nom",
		"QmoveElemsToLayer":"Déplacer les éléments sélectionnés vers le calque '%s' ?",
		"QwantToClear":"Voulez-vous effacer le dessin ?\nL'historique de vos actions sera également effacé !",
		"QwantToOpen":"Do you want to open a new file?\nThis will also erase your undo history!",
		"QerrorsRevertToSource":"Il y a des erreurs d'analyse syntaxique dans votre code-source SVG.\nRevenir au code-source SVG avant modifications ?",
		"QignoreSourceChanges":"Ignorer les modifications faites à la source SVG ?",
		"featNotSupported":"Fonction non supportée",
		"enterNewImgURL":"Entrer la nouvelle URL de l'image",
		"defsFailOnSave": "NOTE : À cause d'un bug de votre navigateur, cette image peut être affichée de façon incorrecte (dégradés ou éléments manquants). Cependant, une fois enregistrée, elle sera correcte.",
		"loadingImage":"Chargement de l'image, veuillez patienter...",
		"saveFromBrowser": "Selectionner \"Enregistrer sous...\" dans votre navigateur pour sauvegarder l'image en tant que fichier %s.",
		"noteTheseIssues": "Notez également les problèmes suivants : ",
		"unsavedChanges": "Il y a des changements non sauvegardés.",
		"enterNewLinkURL": "Entrez la nouvel hyperlien URL",
		"errorLoadingSVG": "Erreur: Impossible de charger le document SVG",
		"URLloadFail": "Impossible de charger l'URL",
		"retrieving": "Récupère \"%s\"..."
	}
});
<commit_msg>Modification  des lignes 17,107,123, 124,125,204,205,206,207<commit_after>svgEditor.readLang({
	lang: "fr",
	dir : "ltr",
	common: {
		"ok": "OK",
		"cancel": "Annuler",
		"key_backspace": "Suppr.",
		"key_del": "Retour Arr.",
		"key_down": "Bas",
		"key_up": "Haut",
		"more_opts": "Plus d'options",
		"url": "URL",
		"width": "Largeur",
		"height": "Hauteur"
	},
	misc: {
		"powered_by": "Propulsé par"
	},
	ui: {
		"toggle_stroke_tools": "Montrer/Cacher plus d'outils de Contour",
		"palette_info": "Cliquer pour changer la couleur de remplissage, Maj-Clic pour changer la couleur de contour",
		"zoom_level": "Changer le niveau de zoom",
		"panel_drag": "Tirer vers la gauche/droite pour redimentionner le panneau"
	},
	properties: {
		"id": "Identifier l'élément",
		"fill_color": "Changer la couleur de remplissage",
		"stroke_color": "Changer la couleur du contour",
		"stroke_style": "Changer le style du contour",
		"stroke_width": "Changer la largeur du contour de 1, Shift-Click pour changer la largeur de 0.1",
		"pos_x": "Changer la position horizontale X",
		"pos_y": "Changer la position verticale Y",
		"linecap_butt": "Terminaison : Sur le nœud",
		"linecap_round": "Terminaison : Arrondie",
		"linecap_square": "Terminaison : Carrée",
		"linejoin_bevel": "Raccord : Biseauté",
		"linejoin_miter": "Raccord : Droit",
		"linejoin_round": "Raccord : Arrondi",
		"angle": "Changer l'angle de rotation",
		"blur": "Changer la valeur du flou gaussien",
		"opacity": "Changer l'opacité de l'élément sélectionné",
		"circle_cx": "Changer la position horizontale cx du cercle",
		"circle_cy": "Changer la position verticale cy du cercle",
		"circle_r": "Changer le rayon du cercle",
		"ellipse_cx": "Changer la position horizontale cx de l'ellipse",
		"ellipse_cy": "Changer la position verticale cy de l'ellipse",
		"ellipse_rx": "Changer le rayon horizontal x de l'ellipse",
		"ellipse_ry": "Changer le rayon vertical y de l'ellipse",
		"line_x1": "Changer la position horizontale x de début de la ligne",
		"line_x2": "Changer la position horizontale x de fin de la ligne",
		"line_y1": "Changer la position verticale y de début de la ligne",
		"line_y2": "Changer la position verticale y de fin de la ligne",
		"rect_height": "Changer la hauteur du rectangle",
		"rect_width": "Changer la largeur du rectangle",
		"corner_radius": "Changer le rayon des coins du rectangle",
		"image_width": "Changer la largeur de l'image",
		"image_height": "Changer la hauteur de l'image",
		"image_url": "Modifier l'URL",
		"node_x": "Changer la positon horizontale x du nœud",
		"node_y": "Changer la position verticale y du nœud",
		"seg_type": "Changer le type du Segment",
		"straight_segments": "Droit",
		"curve_segments": "Courbe",
		"text_contents": "Changer le contenu du texte",
		"font_family": "Changer la famille de police",
		"font_size": "Changer la taille de la police",
		"bold": "Texte en gras",
		"italic": "Texte en italique"
	},
	tools: {
		"main_menu": "Menu principal",
		"bkgnd_color_opac": "Changer la couleur d'arrière-plan / l'opacité",
		"connector_no_arrow": "Sans flèches",
		"fitToContent": "Ajuster au contenu",
		"fit_to_all": "Ajuster au contenu de tous les calques",
		"fit_to_canvas": "Ajuster au canevas",
		"fit_to_layer_content": "Ajuster au contenu du calque",
		"fit_to_sel": "Ajuster à la sélection",
		"align_relative_to": "Aligner par rapport à ...",
		"relativeTo": "Relativement à:",
		"Page": "Page",
		"largest_object": "Plus gros objet",
		"selected_objects": "Objets sélectionnés",
		"smallest_object": "Plus petit objet",
		"new_doc": "Nouvelle image",
		"open_doc": "Ouvrir une image",
		"export_png": "Exporter au format PNG",
		"save_doc": "Enregistrer l'image",
		"import_doc": "Importer un objet SVG",
		"align_to_page": "Aligner l'élément relativement à la Page",
		"align_bottom": "Aligner le bas des objets",
		"align_center": "Centrer verticalement",
		"align_left": "Aligner les côtés gauches",
		"align_middle": "Centrer horizontalement",
		"align_right": "Aligner les côtés droits",
		"align_top": "Aligner le haut des objets",
		"mode_select": "Outil de sélection",
		"mode_fhpath": "Crayon à main levée",
		"mode_line": "Tracer des lignes",
		"mode_connect": "Connecter deux objets",
		"mode_rect": "Outil rectangle",
		"mode_square": "Outils carré",
		"mode_fhrect": "Rectangle main levée",
		"mode_ellipse": "Ellipse",
		"mode_circle": "Cercle",
		"mode_fhellipse": "Ellipse main levée",
		"mode_path": "Dessiner un tracé",
		"mode_shapelib": "Bibliothèque d'images",
		"mode_text": "Outil Texte",
		"mode_image": "Outil Image",
		"mode_zoom": "Zoom",
		"mode_eyedropper": "Outil Pipette",
		"no_embed": "NOTE: Cette image ne peut être incorporée en tant que données. Le contenu affiché sera celui de l'image située à cette adresse",
		"undo": "Annuler l'action",
		"redo": "Refaire l'action",
		"tool_source": "Modifier la source",
		"wireframe_mode": "Mode Fil de Fer",
		"toggle_grid": "Montrer/cacher la grille",
		"clone": "Cloner élement(s)",
		"del": "Supprimer élement(s)",
		"group": "Grouper les éléments",
		"make_link": "Créer (hyper)lien",
		"set_link_url": "Définir le lien URL (laisser vide pour supprimer)",
		"to_path": "Convertir en tracé",
		"reorient_path": "Réorienter le tracé",
		"ungroup": "Dégrouper les éléments",
		"docprops": "Propriétés du document",
		"imagelib": "Bibliothèque d'images",
		"move_bottom": "Déplacer vers le bas",
		"move_top": "Déplacer vers le haut",
		"node_clone": "Cloner le nœud",
		"node_delete": "Supprimer le nœud",
		"node_link": "Rendre les points de contrôle solidaires",
		"add_subpath": "Ajouter un tracé secondaire",
		"openclose_path": "Ouvrir/Fermer sous-chemin",
		"source_save": "Appliquer Modifications",
		"cut": "Couper",
		"copy": "Copier",
		"paste": "Coller",
		"paste_in_place": "Coller sur place",
		"Retour Arr.": "Supprimer",
		"move_front": "Placer au premier plan",
		"move_up": "Placer plus près",
		"move_down": "Placer au dernier plan",
		"move_back": "Placer plus au fond"
	},
	layers: {
		"layer":"Calque",
		"layers": "Calques",
		"del": "Supprimer le calque",
		"move_down": "Descendre le calque",
		"new": "Nouveau calque",
		"rename": "Renommer le calque",
		"move_up": "Monter le calque",
		"dupe": "Dupliquer calque",
		"merge_down": "Fusionner vers le bas",
		"merge_all": "Tout fusionner",
		"move_elems_to": "Déplacer éléments vers:",
		"move_selected": "Déplacer les éléments sélectionnés vers un autre calque"
	},
	config: {
		"image_props": "Propriétés de l'Image",
		"doc_title": "Titre",
		"doc_dims": "Dimensions du canevas",
		"included_images": "Images incorporées",
		"image_opt_embed": "Incorporer les images en tant que données (fichiers locaux)",
		"image_opt_ref": "Utiliser la référence des images ",
		"editor_prefs": "Préférences de l'Éditeur",
		"icon_size": "Taille des icônes",
		"language": "Langue",
		"background": "Toile de fond de l'Éditeur",
		"editor_img_url": "Image URL",
		"editor_bg_note": "Note: La toile de fond n'est pas sauvegardée avec l'image.",
		"icon_large": "Grande",
		"icon_medium": "Moyenne",
		"icon_small": "Petite",
		"icon_xlarge": "Super-Grande",
		"select_predefined": "Sélectionner prédéfinis:",
		"units_and_rulers": "Unités & Règles",
		"show_rulers": "Afficher les règles",
		"base_unit": "Unité de mesure:",
		"grid": "Grille",
		"snapping_onoff": "Épingler oui/non",
		"snapping_stepsize": "Snapping Step-Size:",
		"grid_color": "Couleur de la grille"
	},
	shape_cats: {
		"basic": "Basique",
		"object": "Objets",
		"symbol": "Symboles",
		"arrow": "Flèches",
		"flowchart": "Diagramme de flux",
		"animal": "Animaux",
		"game": "Cartes & Echecs",
		"dialog_balloon": "Bulles de dialogue",
		"electronics": "Electronique",
		"math": "Mathématiques",
		"music": "Musique",
		"misc": "Divers",
		"raphael_1": "raphaeljs.com ensemble 1",
		"raphael_2": "raphaeljs.com ensemble 2"
	},
	imagelib: {
		"select_lib": "Choisir une image dans la bibliothèque",
		"show_list": "Liste de la bibliotèque d'images",
		"import_single": "Importation simple",
		"import_multi": "Importation multiple",
		"open": "Ouvrir en tant que nouveau document"
	},
	notification: {
		"invalidAttrValGiven":"Valeur fournie invalide",
		"noContentToFitTo":"Il n'y a pas de contenu auquel ajuster",
		"dupeLayerName":"Il existe déjà un calque de ce nom !",
		"enterUniqueLayerName":"Veuillez entrer un nom (unique) pour le calque",
		"enterNewLayerName":"Veuillez entrer le nouveau nom du calque",
		"layerHasThatName":"Le calque porte déjà ce nom",
		"QmoveElemsToLayer":"Déplacer les éléments sélectionnés vers le calque '%s' ?",
		"QwantToClear":"Voulez-vous effacer le dessin ?\nL'historique de vos actions sera également effacé !",
		"QwantToOpen":"Voulez-vous ouvrir un nouveau document?\nVous perderez l'historique de vos modifications!",
		"QerrorsRevertToSource":"Il y a des erreurs d'analyse syntaxique dans votre code-source SVG.\nRevenir au code-source SVG avant modifications ?",
		"QignoreSourceChanges":"Ignorer les modifications faites à la source SVG ?",
		"featNotSupported":"Fonction non supportée",
		"enterNewImgURL":"Entrer la nouvelle URL de l'image",
		"defsFailOnSave": "NOTE : À cause d'un bug de votre navigateur, cette image peut être affichée de façon incorrecte (dégradés ou éléments manquants). Cependant, une fois enregistrée, elle sera correcte.",
		"loadingImage":"Chargement de l'image, veuillez patienter...",
		"saveFromBrowser": "Selectionner \"Enregistrer sous...\" dans votre navigateur pour sauvegarder l'image en tant que fichier %s.",
		"noteTheseIssues": "Notez également les problèmes suivants : ",
		"unsavedChanges": "Il y a des changements non sauvegardés.",
		"enterNewLinkURL": "Entrez la nouvel hyperlien URL",
		"errorLoadingSVG": "Erreur: Impossible de charger le document SVG",
		"URLloadFail": "Impossible de charger l'URL",
		"retrieving": "Récupère \"%s\"..."
	}
});
<|endoftext|>


<commit_before>'use strict';

/*global yepnope*/

N.wire.on(module.apiPath + '.create', function recaptcha_create(__, callback) {
  yepnope({
    load: '//www.google.com/recaptcha/api/js/recaptcha_ajax.js',
    complete: function () {
      if (!window.Recaptcha) {
        callback(new Error('Cannot load ReCaptcha script.'));
        return;
      }

      window.Recaptcha.create(N.runtime.recaptcha.public_key, 'recaptcha_div', {
        theme: 'custom'
      });
      callback();
    }
  });
});

N.wire.on(module.apiPath + '.update', function recaptcha_update() {
  if (!window.Recaptcha) {
    return new Error('Cannot update ReCaptcha since it is not loaded.');
  }

  window.Recaptcha.reload();
  window.Recaptcha.focus_response_field();
});
<commit_msg>fixed recaptcha load<commit_after>'use strict';

N.wire.on(module.apiPath + '.create', function recaptcha_create(__, callback) {

  var rc_url = '//www.google.com/recaptcha/api/js/recaptcha_ajax.js';
  var rc_key = N.runtime.recaptcha.public_key;
  var rc_id  = 'recaptcha_div';
  var rc_options = { theme: 'custom' };

  if (window.ReCaptcha) {
    window.Recaptcha.create(rc_key, rc_id, rc_options);
    callback();
    return;
  }

  $.getScript(rc_url)
    .done(function () {
      window.Recaptcha.create(rc_key, rc_id, rc_options);
      callback();
    })
    .fail(function () {
      callback(new Error('Cannot load ReCaptcha script.'));
    });
});

N.wire.on(module.apiPath + '.update', function recaptcha_update() {
  if (!window.Recaptcha) {
    return new Error('Cannot update ReCaptcha since it is not loaded.');
  }

  window.Recaptcha.reload();
  window.Recaptcha.focus_response_field();
});
<|endoftext|>


<commit_before>import argparse from 'argparse'

import models from '../server/models';
import * as stripeGateway from '../server/paymentProviders/stripe/gateway';
import * as constants from '../server/constants/transactions';

/*
  - Find all subscriptions on oldStripeAccount
  - For each one that exists in our database, create new customer based on:
    case 1: Old data
      only has pm.customerId
    case 2: Post-v2 migration data
      has pm.customerId and data.CustomerIdForHost
    case 3: Post-v2 already created a CustomerIdFor this host
      has pm.customerId and data.CustomerIdForHost[newStripeAccount.username]

  - Record new customer info
  - Create plan and add new subscription.
  - Cancel old subscription

*/

let sharedCustomersOnOldStripeAccountCount = 0, sharedCustomersOnOldAndNewStripeAccountsCount = 0,nonSharedCustomersCount = 0, subsUpdatedCount = 0, subsSkippedCount = 0;

const done = (err) => {
  if (err) console.log('err', err);
  console.log('\n')
  console.log('Non-shared customers found: ', nonSharedCustomersCount);
  console.log('Shared customers only on old stripe account found: ', sharedCustomersOnOldStripeAccountCount);
  console.log('Shared customers on both old and new stripe accounts found: ', sharedCustomersOnOldAndNewStripeAccountsCount);
  console.log('Total subscriptions updated: ', subsUpdatedCount);
  console.log('Total subscriptions skipped: ', subsSkippedCount);
  console.log('\n')
  console.log('done!');
  process.exit();
}

const migrateSubscriptions = (options) => {
  const {
    oldHostCollectiveId,
    newHostCollectiveId,
    limit,
    dryRun,
    startingAfter,
    endingBefore,
    plan } = options;

  let oldStripeAccount, newStripeAccount, currentOCSubscription;
  // fetch old stripe account
  return models.ConnectedAccount.findAll({ where: {CollectiveId: oldHostCollectiveId}})
  .then(stripeAccounts => {
    if (!stripeAccounts || stripeAccounts.length === 0) {
      throw new Error('Old stripe account not found');
    }

    if (stripeAccounts.length > 1) {
      throw new Error('More than one old stripe account found');
    }
    oldStripeAccount = stripeAccounts[0]
  })

  // fetch new stripe account
  .then(() => models.ConnectedAccount.findAll({ where: {CollectiveId: newHostCollectiveId}}))
  .then(stripeAccounts => {
    if (!stripeAccounts || stripeAccounts.length === 0) {
      throw new Error('New stripe account not found')
    }

    if (stripeAccounts.length > 1) {
      throw new Error('More than one new stripe account found');
    }
    newStripeAccount = stripeAccounts[0]
  })

  // fetch subscriptions from old stripe account
  .then(() => stripeGateway.getSubscriptionsList(oldStripeAccount, { limit, startingAfter, endingBefore, plan }))

  .then(oldStripeSubscriptionList => {
    console.log("Subscriptions fetched: ", oldStripeSubscriptionList.data.length);
    return oldStripeSubscriptionList.data;
  })
  .each(oldStripeSubscription => {
    if (options.verbose) {
      console.log("\n---OLD SUBSCRIPTION---")
      console.log(oldStripeSubscription);
      console.log("---------END----------")
    } else {
      console.log("\nProcessing subscription: ", oldStripeSubscription.id);
    }

    let platformCustomerId, customerIdOnOldStripeAccount, customerIdOnNewStripeAccount;

    // fetch the subscription from our database
    return models.Subscription.findOne({where: { stripeSubscriptionId: oldStripeSubscription.id}})
      .then(ocSubscription => {
        if (ocSubscription && ocSubscription.isActive) {
          console.log("Subscription found in our DB:", ocSubscription.id)
          currentOCSubscription = ocSubscription;
        } else {
          throw new Error("Subscription not found in our DB: ", oldStripeSubscription.id)
        }
      })

      // fetch order with paymentMethod and other info used for this subscription
      .then(() => {
        return models.Order.find({
          where: {
            SubscriptionId: currentOCSubscription.id
          },
          include: [
          { model: models.Subscription },
          { model: models.PaymentMethod, as: 'paymentMethod'},
          { model: models.User, as: 'createdByUser' }
          ]
        })
      })
      .then(order => {
        const pm = order.paymentMethod;
        const customerIdForHostsList = pm.data && pm.data.customerIdForHost;

        platformCustomerId = pm.customerId;
        customerIdOnOldStripeAccount = customerIdForHostsList && customerIdForHostsList[oldStripeAccount.username];
        customerIdOnNewStripeAccount = customerIdForHostsList && customerIdForHostsList[newStripeAccount.username];

        console.log("platformCustomerId", platformCustomerId)
        console.log("Old customerId", customerIdOnOldStripeAccount);
        console.log("New customerId", customerIdOnNewStripeAccount);

        const processSubscription = () => {

          if (dryRun) {
            console.log('Dry run: Exiting without making any changes on Stripe')
            subsUpdatedCount += 1
            return Promise.resolve();
          }
          // create plan
          const plan = {
            interval: oldStripeSubscription.plan.interval,
            amount: oldStripeSubscription.plan.amount,
            currency: oldStripeSubscription.plan.currency
          }
          return stripeGateway.getOrCreatePlan(newStripeAccount, plan)

          // add a new subscription
          .then(stripeSubscriptionPlan => {
            const subscription = {
              // carryover fields
              plan: stripeSubscriptionPlan.id,
              application_fee_percent: constants.OC_FEE_PERCENT,
              metadata: oldStripeSubscription.metadata,
              // needed to make sure we don't double charge them
              billing_cycle_anchor: oldStripeSubscription.current_period_end,
              prorate: false
            };
            return stripeGateway.createSubscription(
              newStripeAccount,
              customerIdOnNewStripeAccount,
              subscription);
          })

          // store the new stripeSubscription info in our table
          .then(newStripeSubscription => {
            const preMigrationData = currentOCSubscription.data || {};

            // storing this for any subscriptions still in trial period
            // since they have no past data recorded
            preMigrationData.stripeSubscriptionId = oldStripeSubscription.id
            console.log('New stripeSubscriptionId', newStripeSubscription.id);
            return currentOCSubscription.updateAttributes({
              data: Object.assign({}, newStripeSubscription, { preMigrationData }),
              stripeSubscriptionId: newStripeSubscription.id
            });
          })
          // delete new subscription from stripe
          .then(() => stripeGateway.cancelSubscription(
            oldStripeAccount, oldStripeSubscription.id))
          .catch(err => {
            console.log("ERROR: ", err, oldStripeSubscription)
            return err;
          })
        }

        // we shouldn't have any active subscriptions on stripe without customerId
        // on paymentMethod
        if (!platformCustomerId) {
          throw new Error("Payment Method found without Customer Id: ", pm.id);
        }

        // figure out which of the three cases this payment method falls into
        /*
          Case 1: old subscription
            -- pm.data.CustomerIdForHost is null
            -- Check for customerId is moved over by stripe, otherwise skip
          Case 2: post-v2 subscription
            -- pm.data.CustomerIdForHost[oldStripeAccount.username] is not null
            -- Create customerId on newStripeAccount and update PM
          Case 3: post-v2 subscription and user already has a customer Id on new stripe account
            -- pm.data.CustomerIdForHost[oldStripeAccount.username] is not null and pm.data.CustomerIdForHost[newStripeAccount.username] is not null
            -- Nothing needed in terms of Customer Id in this case
        */

        if (!customerIdOnOldStripeAccount && !customerIdOnNewStripeAccount) {
          // Case 3
          console.log("Non-shared customer Id found, checking for customer on new Stripe Account")

          nonSharedCustomersCount += 1;
          return stripeGateway.retrieveCustomer(newStripeAccount, oldStripeSubscription.customer)
            .then(() => {
              return processSubscription();
            })
            // if customer not found, stripe will throw this error
            .catch(err => {
              if (err.message.indexOf('No such customer') !== -1) {
                console.log('Customer not found on new stripe account, skipping')
                subsSkippedCount += 1;
              } else {
                return done(err)
              }
            })
        } else if (customerIdOnOldStripeAccount && !customerIdOnNewStripeAccount) {
          // Case 2
          console.log("Shared Customer id found on old stripe account, creating one on new stripe acount")

          sharedCustomersOnOldStripeAccountCount += 1;
          if (dryRun) {
            console.log('Dry run: Exiting without making any changes on Stripe');
            return Promise.resolve();
          }
          return stripeGateway.createToken(newStripeAccount, platformCustomerId)
            .then(token => stripeGateway.createCustomer(newStripeAccount, token.id, {
              email: order.createdByUser.email,
            }))
            .then(stripeCustomer => {
              console.log("New customerId created: ", stripeCustomer.id);
              customerIdOnNewStripeAccount = stripeCustomer.id;
              const pmData = pm.data;
              pmData.customerIdForHost = Object.assign({}, pmData.customerIdForHost, {[newStripeAccount.username]: stripeCustomer.id})
              return pm.update({data: pmData})
                .then(() => processSubscription())
            })
        } else {
          console.log("Shared Customer id with customer on both old and new stripe account");
          sharedCustomersOnOldAndNewStripeAccountsCount += 1;

          return processSubscription();
        }
      })
  })
}

const run = () => {
  console.log('\nStarting migrate_subscriptions_in_stripe...')

  const parser = new argparse.ArgumentParser({
    addHelp:true,
    description: 'Migrate stripe subscriptions from one host to another'
  });
  parser.addArgument(
    [ '-f', '--fromHostCollectiveId'],
    {
      help: 'Stripe ConnectedAccount Id of Host to move FROM',
      required: true
    }
  );
  parser.addArgument(
    ['-t', '--toHostCollectiveId'],
    {
      help: 'Stripe ConnectedAccount Id of Host to move TO',
      required: true
    }
  );
  parser.addArgument(
    [ '-l', '--limit' ],
    {
      help: 'how many subscriptions to fetch at a time'
    }
  );
  parser.addArgument(
    [ '--notdryrun' ],
    {
      help: 'this flag indicates it\'s not a dry run',
      defaultValue: false,
      action: 'storeConst',
      constant: true
    }
  );
  parser.addArgument(
    [ '--verbose'],
    {
      help: 'verbose output',
      defaultValue: false,
      action: 'storeConst',
      constant: true
    })
  parser.addArgument(
    ['--starting_after'],
    {
      help: 'subscription id on Stripe to start after',
      defaultValue: null
    })
  parser.addArgument(
    ['--ending_before'],
    {
      help: 'subscription id on Stripe to fetch before',
      defaultValue: null
    })
  parser.addArgument(
    ['--plan'],
    {
      help: 'filter subscriptions by this plan',
      defaultValue: null
    })

  const args = parser.parseArgs();

  const options = {
    oldHostCollectiveId: args.fromHostCollectiveId,
    newHostCollectiveId: args.toHostCollectiveId,
    dryRun: !args.notdryrun,
    limit: args.limit || 1,
    verbose: args.verbose,
    startingAfter: args.starting_after,
    endingBefore: args.ending_before,
    plan: args.plan
  }

    console.log("\n\n******************************************")
  if (options.dryRun) {
    console.log(  "***************** dry run ****************")
  } else {
    console.log(  "*************** NOT DRY RUN **************")
    console.log(  "**** THIS WILL MAKE CHANGES ON STRIPE ****")
  }
    console.log(  "******************************************\n\n")

  console.log("Using args: ", options);

  return migrateSubscriptions(options)
  .then(() => done())
  .catch(done)
}

run();<commit_msg>Hot fixing to continue migration<commit_after>import argparse from 'argparse'

import models from '../server/models';
import * as stripeGateway from '../server/paymentProviders/stripe/gateway';
import * as constants from '../server/constants/transactions';

/*
  - Find all subscriptions on oldStripeAccount
  - For each one that exists in our database, create new customer based on:
    case 1: Old data
      only has pm.customerId
    case 2: Post-v2 migration data
      has pm.customerId and data.CustomerIdForHost
    case 3: Post-v2 already created a CustomerIdFor this host
      has pm.customerId and data.CustomerIdForHost[newStripeAccount.username]

  - Record new customer info
  - Create plan and add new subscription.
  - Cancel old subscription

*/

let sharedCustomersOnOldStripeAccountCount = 0, sharedCustomersOnOldAndNewStripeAccountsCount = 0,nonSharedCustomersCount = 0, subsUpdatedCount = 0, subsSkippedCount = 0;

const done = (err) => {
  if (err) console.log('err', err);
  console.log('\n')
  console.log('Non-shared customers found: ', nonSharedCustomersCount);
  console.log('Shared customers only on old stripe account found: ', sharedCustomersOnOldStripeAccountCount);
  console.log('Shared customers on both old and new stripe accounts found: ', sharedCustomersOnOldAndNewStripeAccountsCount);
  console.log('Total subscriptions updated: ', subsUpdatedCount);
  console.log('Total subscriptions skipped: ', subsSkippedCount);
  console.log('\n')
  console.log('done!');
  process.exit();
}

const migrateSubscriptions = (options) => {
  const {
    oldHostCollectiveId,
    newHostCollectiveId,
    limit,
    dryRun,
    startingAfter,
    endingBefore,
    plan } = options;

  let oldStripeAccount, newStripeAccount, currentOCSubscription;
  // fetch old stripe account
  return models.ConnectedAccount.findAll({ where: {CollectiveId: oldHostCollectiveId}})
  .then(stripeAccounts => {
    if (!stripeAccounts || stripeAccounts.length === 0) {
      throw new Error('Old stripe account not found');
    }

    if (stripeAccounts.length > 1) {
      throw new Error('More than one old stripe account found');
    }
    oldStripeAccount = stripeAccounts[0]
  })

  // fetch new stripe account
  .then(() => models.ConnectedAccount.findAll({ where: {CollectiveId: newHostCollectiveId}}))
  .then(stripeAccounts => {
    if (!stripeAccounts || stripeAccounts.length === 0) {
      throw new Error('New stripe account not found')
    }

    if (stripeAccounts.length > 1) {
      throw new Error('More than one new stripe account found');
    }
    newStripeAccount = stripeAccounts[0]
  })

  // fetch subscriptions from old stripe account
  .then(() => stripeGateway.getSubscriptionsList(oldStripeAccount, { limit, startingAfter, endingBefore, plan }))

  .then(oldStripeSubscriptionList => {
    console.log("Subscriptions fetched: ", oldStripeSubscriptionList.data.length);
    return oldStripeSubscriptionList.data;
  })
  .each(oldStripeSubscription => {
    if (options.verbose) {
      console.log("\n---OLD SUBSCRIPTION---")
      console.log(oldStripeSubscription);
      console.log("---------END----------")
    } else {
      console.log("\nProcessing subscription: ", oldStripeSubscription.id);
    }

    let platformCustomerId, customerIdOnOldStripeAccount, customerIdOnNewStripeAccount;

    // fetch the subscription from our database
    return models.Subscription.findOne({where: { stripeSubscriptionId: oldStripeSubscription.id}})
      .then(ocSubscription => {
        if (ocSubscription && ocSubscription.isActive) {
          console.log("Subscription found in our DB:", ocSubscription.id)
          currentOCSubscription = ocSubscription;
        } else {
          throw new Error("Subscription not found in our DB: ", oldStripeSubscription.id)
        }
      })

      // fetch order with paymentMethod and other info used for this subscription
      .then(() => {
        return models.Order.find({
          where: {
            SubscriptionId: currentOCSubscription.id
          },
          include: [
          { model: models.Subscription },
          { model: models.PaymentMethod, as: 'paymentMethod'},
          { model: models.User, as: 'createdByUser' }
          ]
        })
      })
      .then(order => {
        const pm = order.paymentMethod;
        const customerIdForHostsList = pm.data && pm.data.customerIdForHost;

        platformCustomerId = pm.customerId;
        customerIdOnOldStripeAccount = customerIdForHostsList && customerIdForHostsList[oldStripeAccount.username];
        customerIdOnNewStripeAccount = customerIdForHostsList && customerIdForHostsList[newStripeAccount.username];

        console.log("platformCustomerId", platformCustomerId)
        console.log("Old customerId", customerIdOnOldStripeAccount);
        console.log("New customerId", customerIdOnNewStripeAccount);

        const processSubscription = () => {

          if (dryRun) {
            console.log('Dry run: Exiting without making any changes on Stripe')
            subsUpdatedCount += 1
            return Promise.resolve();
          }
          // create plan
          const plan = {
            interval: oldStripeSubscription.plan.interval,
            amount: oldStripeSubscription.plan.amount,
            currency: oldStripeSubscription.plan.currency
          }
          return stripeGateway.getOrCreatePlan(newStripeAccount, plan)

          // add a new subscription
          .then(stripeSubscriptionPlan => {
            const subscription = {
              // carryover fields
              plan: stripeSubscriptionPlan.id,
              application_fee_percent: constants.OC_FEE_PERCENT,
              metadata: oldStripeSubscription.metadata,
              // needed to make sure we don't double charge them
              billing_cycle_anchor: oldStripeSubscription.current_period_end,
              prorate: false
            };
            return stripeGateway.createSubscription(
              newStripeAccount,
              customerIdOnNewStripeAccount,
              subscription);
          })

          // store the new stripeSubscription info in our table
          .then(newStripeSubscription => {
            const preMigrationData = currentOCSubscription.data || {};

            // storing this for any subscriptions still in trial period
            // since they have no past data recorded
            preMigrationData.stripeSubscriptionId = oldStripeSubscription.id
            console.log('New stripeSubscriptionId', newStripeSubscription.id);
            return currentOCSubscription.updateAttributes({
              data: Object.assign({}, newStripeSubscription, { preMigrationData }),
              stripeSubscriptionId: newStripeSubscription.id
            });
          })
          // delete new subscription from stripe
          .then(() => stripeGateway.cancelSubscription(
            oldStripeAccount, oldStripeSubscription.id))
          .catch(err => {
            console.log("ERROR: ", err, oldStripeSubscription)
            return err;
          })
        }

        // we shouldn't have any active subscriptions on stripe without customerId
        // on paymentMethod
        if (!platformCustomerId) {
          throw new Error("Payment Method found without Customer Id: ", pm.id);
        }

        // figure out which of the three cases this payment method falls into
        /*
          Case 1: old subscription
            -- pm.data.CustomerIdForHost is null
            -- Check for customerId is moved over by stripe, otherwise skip
          Case 2: post-v2 subscription
            -- pm.data.CustomerIdForHost[oldStripeAccount.username] is not null
            -- Create customerId on newStripeAccount and update PM
          Case 3: post-v2 subscription and user already has a customer Id on new stripe account
            -- pm.data.CustomerIdForHost[oldStripeAccount.username] is not null and pm.data.CustomerIdForHost[newStripeAccount.username] is not null
            -- Nothing needed in terms of Customer Id in this case
        */

        if (!customerIdOnOldStripeAccount && !customerIdOnNewStripeAccount) {
          // Case 3
          console.log("Non-shared customer Id found, checking for customer on new Stripe Account")

          nonSharedCustomersCount += 1;
          return stripeGateway.retrieveCustomer(newStripeAccount, oldStripeSubscription.customer)
            .then(() => {
              customerIdOnNewStripeAccount = oldStripeSubscription.customer;
              return processSubscription();
            })
            // if customer not found, stripe will throw this error
            .catch(err => {
              if (err.message.indexOf('No such customer') !== -1) {
                console.log('Customer not found on new stripe account, skipping')
                subsSkippedCount += 1;
              } else {
                return done(err)
              }
            })
        } else if (customerIdOnOldStripeAccount && !customerIdOnNewStripeAccount) {
          // Case 2
          console.log("Shared Customer id found on old stripe account, creating one on new stripe acount")

          sharedCustomersOnOldStripeAccountCount += 1;
          if (dryRun) {
            console.log('Dry run: Exiting without making any changes on Stripe');
            return Promise.resolve();
          }
          return stripeGateway.createToken(newStripeAccount, platformCustomerId)
            .then(token => stripeGateway.createCustomer(newStripeAccount, token.id, {
              email: order.createdByUser.email,
            }))
            .then(stripeCustomer => {
              console.log("New customerId created: ", stripeCustomer.id);
              customerIdOnNewStripeAccount = stripeCustomer.id;
              const pmData = pm.data;
              pmData.customerIdForHost = Object.assign({}, pmData.customerIdForHost, {[newStripeAccount.username]: stripeCustomer.id})
              return pm.update({data: pmData})
                .then(() => processSubscription())
            })
        } else {
          console.log("Shared Customer id with customer on both old and new stripe account");
          sharedCustomersOnOldAndNewStripeAccountsCount += 1;

          return processSubscription();
        }
      })
  })
}

const run = () => {
  console.log('\nStarting migrate_subscriptions_in_stripe...')

  const parser = new argparse.ArgumentParser({
    addHelp:true,
    description: 'Migrate stripe subscriptions from one host to another'
  });
  parser.addArgument(
    [ '-f', '--fromHostCollectiveId'],
    {
      help: 'Stripe ConnectedAccount Id of Host to move FROM',
      required: true
    }
  );
  parser.addArgument(
    ['-t', '--toHostCollectiveId'],
    {
      help: 'Stripe ConnectedAccount Id of Host to move TO',
      required: true
    }
  );
  parser.addArgument(
    [ '-l', '--limit' ],
    {
      help: 'how many subscriptions to fetch at a time'
    }
  );
  parser.addArgument(
    [ '--notdryrun' ],
    {
      help: 'this flag indicates it\'s not a dry run',
      defaultValue: false,
      action: 'storeConst',
      constant: true
    }
  );
  parser.addArgument(
    [ '--verbose'],
    {
      help: 'verbose output',
      defaultValue: false,
      action: 'storeConst',
      constant: true
    })
  parser.addArgument(
    ['--starting_after'],
    {
      help: 'subscription id on Stripe to start after',
      defaultValue: null
    })
  parser.addArgument(
    ['--ending_before'],
    {
      help: 'subscription id on Stripe to fetch before',
      defaultValue: null
    })
  parser.addArgument(
    ['--plan'],
    {
      help: 'filter subscriptions by this plan',
      defaultValue: null
    })

  const args = parser.parseArgs();

  const options = {
    oldHostCollectiveId: args.fromHostCollectiveId,
    newHostCollectiveId: args.toHostCollectiveId,
    dryRun: !args.notdryrun,
    limit: args.limit || 1,
    verbose: args.verbose,
    startingAfter: args.starting_after,
    endingBefore: args.ending_before,
    plan: args.plan
  }

    console.log("\n\n******************************************")
  if (options.dryRun) {
    console.log(  "***************** dry run ****************")
  } else {
    console.log(  "*************** NOT DRY RUN **************")
    console.log(  "**** THIS WILL MAKE CHANGES ON STRIPE ****")
  }
    console.log(  "******************************************\n\n")

  console.log("Using args: ", options);

  return migrateSubscriptions(options)
  .then(() => done())
  .catch(done)
}

run();<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for W3C's Speech Input API draft (in progress).
 * http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html
 * This file contains only those functions/properties that are actively
 * used in the Voice Search experiment. Because the draft is under discussion
 * and constantly evolving, this file does not attempt to stay in sync with it.
 *
 * @externs
 */

// W3C Speech API
/**
 * @constructor
 * @extends {UIEvent}
 */
function SpeechInputEvent() {}

/** @type {SpeechInputResultList} */
SpeechInputEvent.prototype.results;

/**
 * @constructor
 */
function SpeechInputResultList() {}

/** @type {number} */
SpeechInputResultList.prototype.length;

/**
 * @constructor
 */
function SpeechInputResult() {}

/** @type {string} */
SpeechInputResult.prototype.utterance;

/** @type {number} */
SpeechInputResult.prototype.confidence;

// HTMLInputElement
/** @type {boolean} */
HTMLInputElement.prototype.webkitspeech;

/** @type {?function (Event)} */
HTMLInputElement.prototype.onwebkitspeechchange;

<commit_msg><commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for W3C's Speech Input 2010 draft API and the
 * 2012 Web Speech draft API (in progress).
 * 2010 Speech Input API:
 * http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html
 * 2012 Web Speech API:
 * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
 * This file contains only those functions/properties that are actively
 * used in the Voice Search experiment. Because the draft is under discussion
 * and constantly evolving, this file does not attempt to stay in sync with it.
 *
 * @externs
 */

// W3C Speech Input API implemented in Chrome M12
/**
 * @constructor
 * @extends {UIEvent}
 */
function SpeechInputEvent() {}

/** @type {SpeechInputResultList} */
SpeechInputEvent.prototype.results;

/**
 * @constructor
 */
function SpeechInputResultList() {}

/** @type {number} */
SpeechInputResultList.prototype.length;

/**
 * @constructor
 */
function SpeechInputResult() {}

/** @type {string} */
SpeechInputResult.prototype.utterance;

/** @type {number} */
SpeechInputResult.prototype.confidence;

// HTMLInputElement
/** @type {boolean} */
HTMLInputElement.prototype.webkitspeech;

/** @type {?function (Event)} */
HTMLInputElement.prototype.onwebkitspeechchange;

// W3C Web Speech API implemented in Chrome M23
/**
 * @constructor
 */
function webkitSpeechGrammar() {}

/** @type {string} */
webkitSpeechGrammar.prototype.src;

/** @type {number} */
webkitSpeechGrammar.prototype.weight;

/**
 * @constructor
 */
function webkitSpeechGrammarList() {}

/** @type {number} */
webkitSpeechGrammarList.prototype.length;

/**
 * @constructor
 */
function SpeechRecognitionAlternative() {}

/** @type {string} */
SpeechRecognitionAlternative.prototype.transcript;

/** @type {number} */
SpeechRecognitionAlternative.prototype.confidence;

/**
 * @constructor
 */
function SpeechRecognitionResult() {}

/** @type {boolean} */
//TODO(user) Replace ['final'] hack with .isFinal once api is updated.
SpeechRecognitionResult.prototype['final'];

/** @type {number} */
SpeechRecognitionResult.prototype.length;

/** @type {Document} */
SpeechRecognitionResult.prototype.emma;

/** @type {function(number): SpeechRecognitionAlternative} */
SpeechRecognitionResult.prototype.item = function(index) {};

/**
 * @constructor
 */
function SpeechRecognitionResultList() {}

/** @type {number} */
SpeechRecognitionResultList.prototype.length;

/** @type {function(number): SpeechRecognitionResult} */
SpeechRecognitionResultList.prototype.item = function(index) {};

/**
 * @constructor
 * @extends {Event}
 */
function webkitSpeechRecognitionEvent() {}

/** @type {SpeechRecognitionResult} */
webkitSpeechRecognitionEvent.prototype.result;

/** @type {number} */
webkitSpeechRecognitionEvent.prototype.resultIndex;

/** @type {SpeechRecognitionResultList} */
webkitSpeechRecognitionEvent.prototype.resultHistory;

/**
 * @constructor
 */
function webkitSpeechRecognitionError() {}

/** @type {number} */
webkitSpeechRecognitionError.prototype.code;

/** @type {number} */
webkitSpeechRecognitionError.prototype.OTHER;

/** @type {number} */
webkitSpeechRecognitionError.prototype.NO_SPEECH;

/** @type {number} */
webkitSpeechRecognitionError.prototype.ABORTED;

/** @type {number} */
webkitSpeechRecognitionError.prototype.AUDIO_CAPTURE;

/** @type {number} */
webkitSpeechRecognitionError.prototype.NETWORK;

/** @type {number} */
webkitSpeechRecognitionError.prototype.NOT_ALLOWED;

/** @type {number} */
webkitSpeechRecognitionError.prototype.SERVICE_NOT_ALLOWED;

/** @type {number} */
webkitSpeechRecognitionError.prototype.BAD_GRAMMAR;

/** @type {number} */
webkitSpeechRecognitionError.prototype.LANGUAGE_NOT_SUPPORTED;

/** @type {string} */
webkitSpeechRecognitionError.prototype.message;

/**
 * @constructor
 */
function webkitSpeechRecognition() {}

/** @type {webkitSpeechGrammarList} */
webkitSpeechRecognition.prototype.grammars;

/** @type {string} */
webkitSpeechRecognition.prototype.lang;

/** @type {boolean} */
webkitSpeechRecognition.prototype.continuous;

/** @type {boolean} */
webkitSpeechRecognition.prototype.interimResults;

/** @type {number} */
webkitSpeechRecognition.prototype.maxAlternatives;

/** @type {function(): undefined} */
webkitSpeechRecognition.prototype.start;

/** @type {function(): undefined} */
webkitSpeechRecognition.prototype.stop;

/** @type {function(): undefined} */
webkitSpeechRecognition.prototype.abort;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onaudiostart;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onsoundstart;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onspeechstart;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onaudioend;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onsoundend;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onspeechend;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onend;

/** @type {function(Event): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onstart;

/** @type {function(webkitSpeechRecognitionEvent): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onnomatch;

/** @type {function(webkitSpeechRecognitionEvent): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onresult;

/** @type {function(webkitSpeechRecognitionError): (boolean|undefined)} */
webkitSpeechRecognition.prototype.onerror;

<|endoftext|>


<commit_before>const gulp = require('gulp')
const hb = require('gulp-hb')
const rename = require('gulp-rename')
const include = require('gulp-file-include')
const plumber = require('gulp-plumber')
const replace = require('gulp-replace')
const report = require('../report-error.js')
const browserSync = require('browser-sync')

const srcIndex = 'src/html/index.hbs'
const svgPath = `${process.cwd()}/svg/`

gulp.task('html-dev', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		.helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: '', timestamp: Date.now() })

	return gulp.src(srcIndex)
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('dev'))
		.pipe(browserSync.reload({ stream: true }))
})

gulp.task('html-dist', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		.helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() })

	return gulp.src(srcIndex)
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('.tmp'))
})

gulp.task('html-boilerplate', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		// .helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() })

	return gulp.src(srcIndex)
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(replace('<!-- boilerplate.css -->', `
	<link rel="stylesheet" href="critical.css" />
	<link rel="stylesheet" href="bundle.css" />
		`))
		.pipe(replace("<link rel='stylesheet' href='bundle.css' />", ''))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('boilerplate'))
})

gulp.task('html-style-guide', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		// .helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() })

	return gulp.src('./src/html/style-guide.hbs')
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('docs'))
})<commit_msg>allow multiple page<commit_after>const gulp = require('gulp');
const hb = require('gulp-hb');
const rename = require('gulp-rename');
const include = require('gulp-file-include');
const plumber = require('gulp-plumber');
const replace = require('gulp-replace');
const report = require('../report-error.js');
const browserSync = require('browser-sync');

const srcIndex = 'src/html/*.hbs';
const svgPath = `${process.cwd()}/svg/`;

gulp.task('html-dev', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		.helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: '', timestamp: Date.now() });

	return (
		gulp
			.src(srcIndex)
			.pipe(plumber({ errorHandler: report }))
			.pipe(hbStream)
			.pipe(include({ basepath: svgPath }))
			.pipe(
				rename(path => {
					path.extname = '.html';
				})
			)
			// .pipe(rename('index.html'))
			.pipe(gulp.dest('dev'))
			.pipe(browserSync.reload({ stream: true }))
	);
});

gulp.task('html-dist', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		.helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() });

	return gulp
		.src(srcIndex)
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(
			rename(path => {
				path.extname = '.html';
			})
		)
		.pipe(gulp.dest('.tmp'));
});

gulp.task('html-boilerplate', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		// .helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() });

	return gulp
		.src(srcIndex)
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(
			replace(
				'<!-- boilerplate.css -->',
				`
	<link rel="stylesheet" href="critical.css" />
	<link rel="stylesheet" href="bundle.css" />
		`
			)
		)
		.pipe(replace('<link rel=\'stylesheet\' href=\'bundle.css\' />', ''))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('boilerplate'));
});

gulp.task('html-style-guide', () => {
	const hbStream = hb()
		.partials('./src/html/partials/**/*.hbs')
		// .helpers('./src/html/helpers/*.js')
		.data('./template-data/**/*.{js,json}')
		.data({ basepath: 'https://pudding.cool/', timestamp: Date.now() });

	return gulp
		.src('./src/html/style-guide.hbs')
		.pipe(plumber({ errorHandler: report }))
		.pipe(hbStream)
		.pipe(include({ basepath: svgPath }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('docs'));
});
<|endoftext|>