<commit_before>/**
 * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 */
openerp.base.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.base.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.base.ListView.include(/** @lends openerp.base.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.base.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            this._super(results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.fields_view.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        }
    });

    openerp.base.ListView.Groups.include(/** @lends openerp.base.ListView.Groups# */{
        passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    openerp.base.ListView.List.include(/** @lends openerp.base.ListView.List */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super(event);
            }
            this.edit_record();
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_index !== null) {
                this.reload_record_at_index(this.edition_index).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_index;
                delete self.edition;
            });
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': $(row).data('id'),
                        'data-index': $(row).data('index'),
                        'class': $(row).attr('class') + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button.oe-edit-row-cancel', 'click', function () {
                        self.cancel_edition();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function (e) {
                        switch (e.which) {
                            case KEY_RETURN:
                                self.save_row(true);
                                break;
                            case KEY_ESCAPE:
                                self.cancel_edition();
                                break;
                            default:
                                return;
                        }
                    });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable === 'top') {
                    self.$current.prepend($new_row);
                } else if (self.options.editable) {
                    self.$current.append($new_row);
                }
                self.edition = true;
                self.edition_index = self.dataset.index;
                self.edition_form = _.extend(new openerp.base.FormView(
                        self, $new_row.attr('id'), self.dataset, false), {
                    template: 'ListView.row.form',
                    registry: openerp.base.list.form.widgets
                });
                $.when(self.edition_form.on_loaded({fields_view: self.get_form_fields_view()})).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:first').removeClass('oe-field-cell').end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (record_id, index) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        record = new openerp.base.list.Record({id: id});
                        self.records.add(record, {at: index});
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                if (result.created && !self.edition_index) {
                    self.records.add({id: result.result},
                        {at: self.options.editable === 'top' ? 0 : null});
                    self.edition_index = self.dataset.index;
                }
                self.handle_onwrite(self.dataset.ids[self.dataset.index],
                                    self.dataset.index);
                self.cancel_pending_edition().then(function () {
                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    self.dataset.next();
                    self.edit_record();
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function () {
            this.render_row_as_form(
                this.$current.children(':eq(' + this.dataset.index + ')'));
            $(this).trigger(
                'edit',
                [this.records.at(this.dataset.index).get('id'), this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    if (!openerp.base.list) {
        openerp.base.list = {};
    }
    if (!openerp.base.list.form) {
        openerp.base.list.form = {};
    }
    openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.base.form.widgets;
    openerp.base.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.base.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.base.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.base.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.base.list.form.' + key;

        openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = !!this.modifiers.tree_invisible;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<commit_msg>[IMP] remember to insert the relevant record ids into the dataset if they're not there<commit_after>/**
 * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 */
openerp.base.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.base.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.base.ListView.include(/** @lends openerp.base.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.base.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            this._super(results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.fields_view.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        }
    });

    openerp.base.ListView.Groups.include(/** @lends openerp.base.ListView.Groups# */{
        passtrough_events: openerp.base.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    openerp.base.ListView.List.include(/** @lends openerp.base.ListView.List */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super(event);
            }
            this.edit_record();
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_index !== null) {
                this.reload_record_at_index(this.edition_index).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_index;
                delete self.edition;
            });
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': $(row).data('id'),
                        'data-index': $(row).data('index'),
                        'class': $(row).attr('class') + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button.oe-edit-row-cancel', 'click', function () {
                        self.cancel_edition();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function (e) {
                        switch (e.which) {
                            case KEY_RETURN:
                                self.save_row(true);
                                break;
                            case KEY_ESCAPE:
                                self.cancel_edition();
                                break;
                            default:
                                return;
                        }
                    });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable === 'top') {
                    self.$current.prepend($new_row);
                } else if (self.options.editable) {
                    self.$current.append($new_row);
                }
                self.edition = true;
                self.edition_index = self.dataset.index;
                self.edition_form = _.extend(new openerp.base.FormView(
                        self, $new_row.attr('id'), self.dataset, false), {
                    template: 'ListView.row.form',
                    registry: openerp.base.list.form.widgets
                });
                $.when(self.edition_form.on_loaded({fields_view: self.get_form_fields_view()})).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:first').removeClass('oe-field-cell').end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (record_id, index) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        record = new openerp.base.list.Record({id: id});
                        self.records.add(record, {at: index});
                        self.dataset.ids.splice(index, 0, id);
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                if (result.created && !self.edition_index) {
                    self.records.add({id: result.result},
                        {at: self.options.editable === 'top' ? 0 : null});
                    self.edition_index = self.dataset.index;
                }
                self.handle_onwrite(self.dataset.ids[self.dataset.index],
                                    self.dataset.index);
                self.cancel_pending_edition().then(function () {
                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    self.dataset.next();
                    self.edit_record();
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function () {
            this.render_row_as_form(
                this.$current.children(':eq(' + this.dataset.index + ')'));
            $(this).trigger(
                'edit',
                [this.records.at(this.dataset.index).get('id'), this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    if (!openerp.base.list) {
        openerp.base.list = {};
    }
    if (!openerp.base.list.form) {
        openerp.base.list.form = {};
    }
    openerp.base.list.form.WidgetFrame = openerp.base.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.base.form.widgets;
    openerp.base.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.base.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.base.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.base.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.base.list.form.' + key;

        openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = !!this.modifiers.tree_invisible;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<|endoftext|>


<commit_before>/* ----------------- My maps edition ------------------- */

function setMapItemVisibility(elt, props) {
  //var isVisible = e.target.getAttribute("isVisible") == "true";
  var isVisible = props.on;
  elt.attr("isVisible", "" + isVisible);
  elt.text(isVisible ? "visibility" : "visibility_off");
  var parent = elt.parent(".map-item");
  if (isVisible && parent.length) {
    parent.removeClass("map-invisible");
  } else {
    parent.addClass("map-invisible");
  }
}

function addMymapsItem(name, props, addHandlers) {
  var mapitem = "
";
  mapitem += "drag_indicator ";
  mapitem += "" + name + " ";
  mapitem += "?? ";
  if (props.in == MAP_MY) {
    mapitem += "create ";
    mapitem += "share ";
    mapitem += "delete ";
  }
  mapitem += "
";
  $("#mymaps-list").append(mapitem);
  var newitem = $("#mymaps-list").children().last();
  setMapItemVisibility(newitem.find(".map-visibility"), props);
  if (addHandlers) {
    addMapItemHandlers(newitem);
  }
}

function getMapName(elt) {
  return elt.parentNode.querySelector(".map-name").innerText;
}

function getMapProps(elt) {
  var name = getMapName(elt);
  var idx = mapsListNames.indexOf(name);
  return mapsListProps[idx];
}

function getMapItem(name) {
  var res = undefined;
  $("#mymaps-list .map-name").each(function(i, v) {
    if (name == v.innerText) {
      res = $(v).parents("li");
      return true;
    }
  });
  return res;
}

function changeMymapsItem(oldname, newname) {
  var mapItem = getMapItem(oldname);
  if (mapItem) {
    if (newname) {
      // name changed
      mapItem.find(".map-name").text(newname);
    } else {
      // item deleted
      mapItem.remove();
    }
  }
}

function showMapsList() {
  $("#mymaps-list").empty();
  mapsForEach(function(name, value) {
    addMymapsItem(name, value);
  });
  addMapItemHandlers($('#mymaps-list li'));
}

function toggleMapVisibility(e) {
  var mprops = getMapProps(e.target);
  mprops.on = !mprops.on;
  saveMapList();
  setMapItemVisibility($(e.target), mprops)
}

function editMapItem(e) {
  var name = getMapName(e.target);
  editMymap(name);
}
function deleteMapItem(e) {
  var name = getMapName(e.target);
  deleteMymap(name);
}
function shareMapItem(e) {
  var name = getMapName(e.target);
  openExportMymaps(null, name);
}

function addMapItemHandlers(selector) {
  selector.find(".map-edit").click(editMapItem);
  selector.find(".map-visibility").click(toggleMapVisibility);
  selector.find(".map-delete").click(deleteMapItem);
  selector.find(".map-share").click(shareMapItem);
}

// ----------------------- Personal map edition ----------------------

var mymap;

function initCrsSelector() {
  var crsSelect = $("#mymap-crs")[0];
  for (var i = CrsValues.length - 1; i >=0; i--) {
    var crs = CrsValues[i];
    addSelectOption(crsSelect, crs ? crs.code : "");
  }
}
initCrsSelector();

function openMymapBox() {
  $("#mymap-name").val(mymap.name);
  $("#mymap-url").val(mymap.url);
  $("#mymap-minz").val(mymap.options.minZoom);
  $("#mymap-maxz").val(mymap.options.maxZoom);
  $("#mymap-layers").val(mymap.options.layers);
  $("#mymap-layer").val(mymap.options.layer);
  $("#mymap-tilematrixSet").val(mymap.options.tilematrixSet);
  $("#mymap-crs").val(mymap.options.crs);
  $("#mymap-styles").val(mymap.options.styles);
  $("#mymap-style").val(mymap.options.style);
  $("#mymap-format").val(mymap.options.format);
  $("#mymap-attr").val(mymap.options.attribution);
  $("#mymap-box input:radio[name=mymap-type][value=" + mymap.type + "]").prop('checked', true);
  $("#mymap-box").show();
  $("#mymap-name").focus();
  changeMymapType();
}

function newMymap(evt) {
  mymap = { options: {} };
  openMymapBox();
}

function editMymap(mymapname) {
  if (mymapname) {
    mymap = mymaps[mymapname];
    if (mymap) {
      mymap.name = mymapname;
      openMymapBox();
    }
  }
}

var mymapsInputs = $("#mymap-box input");
mymapsInputs.on("invalid", function(evt) {
  var invalidInput = $(evt.target);
  invalidInput.addClass("invalid");
  invalidInput.focus();
});
mymapsInputs.on("input", function(evt) {
  if (evt.target.checkValidity()) {
    $(evt.target).removeClass("invalid");
  }
});

mymapsInputs.keyup(function(event) {
  if (event.which == 27) {
    cancelMymapBox();
    event.stopPropagation();
  } else if (event.keyCode == 13) {
    validateMymapBox();
  }
});

function validateMymapBox(evt) {
  var valid = true;
  // check validity on displayed inputs
  $("#mymap-box input:visible").each(function(i, v) {
    var display = $(v).css('display');
    if ((display != "none") && (!v.checkValidity())) {
      valid = false;
      return false;
    }
  });
  if (valid) {
    //mymapsInputs.removeClass("invalid");
    $("#mymap-box").hide();
    var oldname = mymap.name;
    var newname = $("#mymap-name").val().trim();
    mymap[name] = undefined;
    mymap.url = $("#mymap-url").val().trim();
    mymap.type = $('input:radio[name=mymap-type]:checked').val();
    mymap.options.minZoom= $("#mymap-minz").val().trim();
    mymap.options.maxZoom = $("#mymap-maxz").val().trim();
    if (mymap.type === "wms") {
      mymap.options.layers = $("#mymap-layers").val().trim();
      mymap.options.crs = $("#mymap-crs").val().trim();
      mymap.options.styles = $("#mymap-styles").val().trim();
      mymap.options.format = $("#mymap-format").val().trim();
    } else if (mymap.type === "wmts") {
      mymap.options.layer = $("#mymap-layer").val().trim();
      mymap.options.tilematrixSet = $("#mymap-tilematrixSet").val().trim();
      mymap.options.style = $("#mymap-style").val().trim();
      mymap.options.format = $("#mymap-format").val().trim();
    }
    mymap.options.attribution = $("#mymap-attr").val().trim();
    if (oldname && mymaps[oldname]) {
      if (oldname != newname) {
        // rebuild object to preserve order
        var tmp = {};
        objectForEach(mymaps, function(name, value) {
          if (name == oldname) {
            tmp[newname] = mymap;
          } else {
            tmp[name] = value;
          }
        });
        mymaps = tmp;
        renameMapListEntry(oldname, newname);
        changeMymapsItem(oldname, newname);
        saveMapList();
      } else {
        mymaps[newname] = mymap;
      }
      ga('send', 'event', 'map', 'edit');
    } else {
      mymaps[newname] = mymap;
      addMymapsItem(newname, addMapListEntry(newname, MAP_MY, true), true);
      saveMapList();
      ga('send', 'event', 'map', 'add');
    }
    saveJsonValOpt("wt.mymaps", mymaps);
  }
}

function cancelMymapBox(evt) {
  $("#mymap-box").hide();
  mymapsInputs.removeClass("invalid");
  mymap = undefined;
}

function deleteMymap(mymapname) {
  if (mymapname) {
    mymap = mymaps[mymapname];
    if (mymap && confirm("Delete " + mymapname + "?")) {
      mymaps[mymapname] = undefined;
      saveJsonValOpt("wt.mymaps", mymaps);
      // delete map in lists
      delMapListEntry(getMapListEntryIndex(mymapname));
      changeMymapsItem(mymapname);
      saveMapList();
      ga('send', 'event', 'map', 'delete');
    }
  }
}

function changeMymapType(evt) {
  var type =  $('input:radio[name=mymap-type]:checked').val();
  $(".map-wmts").hide();
  $(".map-wms").hide();
  if (type != "base") {
    $(".map-" + type).show();
  }
}

function deleteAllMymaps(evt) {
  if (!$.isEmptyObject(mymaps) &&
      confirm("Delete all your personal maps?")) {
    mymaps = {};
    saveJsonValOpt("wt.mymaps", undefined);
    ga('send', 'event', 'map', 'deleteall');
    getMapList();
    showMapsList()
  }
}
function reorderMapList(evt) {
  if (confirm("Re-order map list according to defaults, with personal maps at the end?")) {
    ga('send', 'event', 'map', 'reorder');
    resetMapList();
    getMapList();
    showMapsList();
  }
}

$("#mymap-ok").click(validateMymapBox);
$("#mymap-cancel").click(cancelMymapBox);

$("#mymaps-new").click(newMymap);
$("#mymaps-deleteall").click(deleteAllMymaps);

$("#maps-reorder").click(reorderMapList);
$("input:radio[name=mymap-type]").change(changeMymapType);

// ---------------- Import / export my maps

function openExportMymaps(evt, mymapname) {
  var toexport = mymapname ? { mymapname : mymaps[mymapname] } : mymaps;
  if (!$.isEmptyObject(toexport)) {
    var json = JSON.stringify(toexport);
    var data = b64EncodeUnicode(json);
    $("#input-text").text("Copy and share map data below (Ctrl+C & Enter):");
    $("#input-ok").hide();
    $("#input-val").val(data);
    $("#input-box").show();
    $("#input-val").focus();
    $("#input-val").select();
    $("#input-val").attr("readonly", "readonly");
    $(".prompt-content .copyonclick").show();
    ga('send', 'event', 'map', 'export', undefined, mymaps.length);
  }
}

function openImportMymaps() {
  $("#input-text").text("Paste exported map data (Ctrl+V & Enter):");
  $("#input-val").val("");
  $("#input-ok").show();
  $("#input-box").show();
  $("#input-val").focus();
  $(".prompt-content .copyonclick").hide();
  $("#input-val").attr("readonly", undefined);
}

function importMymaps() {
  var imported = 0;
  try {
    var data = $("#input-val").val().trim();
    var importedMymaps = data ? JSON.parse(b64DecodeUnicode(data)) : {};
    objectForEach(importedMymaps, function(name, value) {
      var overwrite = mymaps[name];
      var msg = overwrite ? "Overwrite " : "Import ";
      if (confirm(msg + name + "?")) {
        if (overwrite) {
          changeMymapsItem(name, name);
        } else {
          addMymapsItem(name, addMapListEntry(name, MAP_MY, true), true);
        }
        mymaps[name] = value;
        imported++;
      }
    });
  } catch (ex) {
    error("Invalid import data");
  }
  if (imported > 0) {
    saveJsonValOpt("wt.mymaps", mymaps);
    ga('send', 'event', 'map', 'import', undefined, imported);
    saveMapList();
  }
  $("#input-box").hide();
}

$("#input-box-close").click(function() {
  $("#input-box").hide();
  return false;
});

$("#input-val").keyup(function(event) {
  if (event.which == 27) {
    $("#input-box").hide();
  } else if (event.keyCode == 13) {
    var isImport = $("#input-ok").is(":visible");
    $("#input-box").hide();
    if (isImport) {
      importMymaps();
    }
  }
});

$("#input-ok").click(importMymaps);

$("#mymaps-import").click(openImportMymaps);
$("#mymaps-export").click(openExportMymaps);

if (!supportsBase64() || !JSON || !JSON.parse || !JSON.stringify) {
  $("#mymaps-import").attr("disabled", "disabled");
  $("#mymaps-export").attr("disabled", "disabled");
}

// ------------------- ready?

$(document).ready(function() {
  showMapsList();
  // ----- drag & drop list -----
  $("#mymaps-list").sortable({
    //scroll: true,
    handle: ".map-drag, .map-name",
    update: function(evt) {
      // get map-item
      var item = $(evt.target).parents(".map-item");
      // NOTE: item may be detached from the list at this step
      // moved item name
      var name = item.find(".map-name").text();
      // get new index
      var toIdx = getMapItem(name).index();
      // get old index
      var fromIdx = getMapListEntryIndex(name);
      if (fromIdx != toIdx) {
        // update list data
        moveMapListEntry(fromIdx, toIdx);
        saveMapList();
      }
    }
  });
});

$(window).on("unload", function() {
  $("#mymaps-list").sortable('destroy');
});
<commit_msg>check new map name does not override another one<commit_after>/* ----------------- My maps edition ------------------- */

function setMapItemVisibility(elt, props) {
  //var isVisible = e.target.getAttribute("isVisible") == "true";
  var isVisible = props.on;
  elt.attr("isVisible", "" + isVisible);
  elt.text(isVisible ? "visibility" : "visibility_off");
  var parent = elt.parent(".map-item");
  if (isVisible && parent.length) {
    parent.removeClass("map-invisible");
  } else {
    parent.addClass("map-invisible");
  }
}

function addMymapsItem(name, props, addHandlers) {
  var mapitem = "
";
  mapitem += "drag_indicator ";
  mapitem += "" + name + " ";
  mapitem += "?? ";
  if (props.in == MAP_MY) {
    mapitem += "create ";
    mapitem += "share ";
    mapitem += "delete ";
  }
  mapitem += "
";
  $("#mymaps-list").append(mapitem);
  var newitem = $("#mymaps-list").children().last();
  setMapItemVisibility(newitem.find(".map-visibility"), props);
  if (addHandlers) {
    addMapItemHandlers(newitem);
  }
}

function getMapName(elt) {
  return elt.parentNode.querySelector(".map-name").innerText;
}

function getMapProps(elt) {
  var name = getMapName(elt);
  var idx = mapsListNames.indexOf(name);
  return mapsListProps[idx];
}

function getMapItem(name) {
  var res = undefined;
  $("#mymaps-list .map-name").each(function(i, v) {
    if (name == v.innerText) {
      res = $(v).parents("li");
      return true;
    }
  });
  return res;
}

function changeMymapsItem(oldname, newname) {
  var mapItem = getMapItem(oldname);
  if (mapItem) {
    if (newname) {
      // name changed
      mapItem.find(".map-name").text(newname);
    } else {
      // item deleted
      mapItem.remove();
    }
  }
}

function showMapsList() {
  $("#mymaps-list").empty();
  mapsForEach(function(name, value) {
    addMymapsItem(name, value);
  });
  addMapItemHandlers($('#mymaps-list li'));
}

function toggleMapVisibility(e) {
  var mprops = getMapProps(e.target);
  mprops.on = !mprops.on;
  saveMapList();
  setMapItemVisibility($(e.target), mprops)
}

function editMapItem(e) {
  var name = getMapName(e.target);
  editMymap(name);
}
function deleteMapItem(e) {
  var name = getMapName(e.target);
  deleteMymap(name);
}
function shareMapItem(e) {
  var name = getMapName(e.target);
  openExportMymaps(null, name);
}

function addMapItemHandlers(selector) {
  selector.find(".map-edit").click(editMapItem);
  selector.find(".map-visibility").click(toggleMapVisibility);
  selector.find(".map-delete").click(deleteMapItem);
  selector.find(".map-share").click(shareMapItem);
}

// ----------------------- Personal map edition ----------------------

var mymap;

function initCrsSelector() {
  var crsSelect = $("#mymap-crs")[0];
  for (var i = CrsValues.length - 1; i >=0; i--) {
    var crs = CrsValues[i];
    addSelectOption(crsSelect, crs ? crs.code : "");
  }
}
initCrsSelector();

function openMymapBox() {
  $("#mymap-name").val(mymap.name);
  $("#mymap-url").val(mymap.url);
  $("#mymap-minz").val(mymap.options.minZoom);
  $("#mymap-maxz").val(mymap.options.maxZoom);
  $("#mymap-layers").val(mymap.options.layers);
  $("#mymap-layer").val(mymap.options.layer);
  $("#mymap-tilematrixSet").val(mymap.options.tilematrixSet);
  $("#mymap-crs").val(mymap.options.crs);
  $("#mymap-styles").val(mymap.options.styles);
  $("#mymap-style").val(mymap.options.style);
  $("#mymap-format").val(mymap.options.format);
  $("#mymap-attr").val(mymap.options.attribution);
  $("#mymap-box input:radio[name=mymap-type][value=" + mymap.type + "]").prop('checked', true);
  $("#mymap-box").show();
  $("#mymap-name").focus();
  changeMymapType();
}

function newMymap(evt) {
  mymap = { options: {} };
  openMymapBox();
}

function editMymap(mymapname) {
  if (mymapname) {
    mymap = mymaps[mymapname];
    if (mymap) {
      mymap.name = mymapname;
      openMymapBox();
    }
  }
}

var mymapsInputs = $("#mymap-box input");
mymapsInputs.on("invalid", function(evt) {
  var invalidInput = $(evt.target);
  invalidInput.addClass("invalid");
  invalidInput.focus();
});
mymapsInputs.on("input", function(evt) {
  if (evt.target.checkValidity()) {
    $(evt.target).removeClass("invalid");
  }
});

mymapsInputs.keyup(function(event) {
  if (event.which == 27) {
    cancelMymapBox();
    event.stopPropagation();
  } else if (event.keyCode == 13) {
    validateMymapBox();
  }
});

function validateMymapBox(evt) {
  var valid = true;
  // check validity on displayed inputs
  $("#mymap-box input:visible").each(function(i, v) {
    var display = $(v).css('display');
    if ((display != "none") && (!v.checkValidity())) {
      valid = false;
      return false;
    }
  });
  var oldname = mymap.name;
  var newname = $("#mymap-name").val().trim();
  if ((oldname != newname) && (getMapListEntryIndex(newname) >= 0)) {
    $("#mymap-name").trigger("invalid");
    warn("Map name already used: " + newname);
    valid = false;
  }
  if (valid) {
    //mymapsInputs.removeClass("invalid");
    $("#mymap-box").hide();
    mymap[name] = undefined;
    mymap.url = $("#mymap-url").val().trim();
    mymap.type = $('input:radio[name=mymap-type]:checked').val();
    mymap.options.minZoom= $("#mymap-minz").val().trim();
    mymap.options.maxZoom = $("#mymap-maxz").val().trim();
    if (mymap.type === "wms") {
      mymap.options.layers = $("#mymap-layers").val().trim();
      mymap.options.crs = $("#mymap-crs").val().trim();
      mymap.options.styles = $("#mymap-styles").val().trim();
      mymap.options.format = $("#mymap-format").val().trim();
    } else if (mymap.type === "wmts") {
      mymap.options.layer = $("#mymap-layer").val().trim();
      mymap.options.tilematrixSet = $("#mymap-tilematrixSet").val().trim();
      mymap.options.style = $("#mymap-style").val().trim();
      mymap.options.format = $("#mymap-format").val().trim();
    }
    mymap.options.attribution = $("#mymap-attr").val().trim();
    if (oldname && mymaps[oldname]) {
      if (oldname != newname) {
        // rebuild object to preserve order
        var tmp = {};
        objectForEach(mymaps, function(name, value) {
          if (name == oldname) {
            tmp[newname] = mymap;
          } else {
            tmp[name] = value;
          }
        });
        mymaps = tmp;
        renameMapListEntry(oldname, newname);
        changeMymapsItem(oldname, newname);
        saveMapList();
      } else {
        mymaps[newname] = mymap;
      }
      ga('send', 'event', 'map', 'edit');
    } else {
      mymaps[newname] = mymap;
      addMymapsItem(newname, addMapListEntry(newname, MAP_MY, true), true);
      saveMapList();
      ga('send', 'event', 'map', 'add');
    }
    saveJsonValOpt("wt.mymaps", mymaps);
  }
}

function cancelMymapBox(evt) {
  $("#mymap-box").hide();
  mymapsInputs.removeClass("invalid");
  mymap = undefined;
}

function deleteMymap(mymapname) {
  if (mymapname) {
    mymap = mymaps[mymapname];
    if (mymap && confirm("Delete " + mymapname + "?")) {
      mymaps[mymapname] = undefined;
      saveJsonValOpt("wt.mymaps", mymaps);
      // delete map in lists
      delMapListEntry(getMapListEntryIndex(mymapname));
      changeMymapsItem(mymapname);
      saveMapList();
      ga('send', 'event', 'map', 'delete');
    }
  }
}

function changeMymapType(evt) {
  var type =  $('input:radio[name=mymap-type]:checked').val();
  $(".map-wmts").hide();
  $(".map-wms").hide();
  if (type != "base") {
    $(".map-" + type).show();
  }
}

function deleteAllMymaps(evt) {
  if (!$.isEmptyObject(mymaps) &&
      confirm("Delete all your personal maps?")) {
    mymaps = {};
    saveJsonValOpt("wt.mymaps", undefined);
    ga('send', 'event', 'map', 'deleteall');
    getMapList();
    showMapsList()
  }
}
function reorderMapList(evt) {
  if (confirm("Re-order map list according to defaults, with personal maps at the end?")) {
    ga('send', 'event', 'map', 'reorder');
    resetMapList();
    getMapList();
    showMapsList();
  }
}

$("#mymap-ok").click(validateMymapBox);
$("#mymap-cancel").click(cancelMymapBox);

$("#mymaps-new").click(newMymap);
$("#mymaps-deleteall").click(deleteAllMymaps);

$("#maps-reorder").click(reorderMapList);
$("input:radio[name=mymap-type]").change(changeMymapType);

// ---------------- Import / export my maps

function openExportMymaps(evt, mymapname) {
  var toexport = mymapname ? { mymapname : mymaps[mymapname] } : mymaps;
  if (!$.isEmptyObject(toexport)) {
    var json = JSON.stringify(toexport);
    var data = b64EncodeUnicode(json);
    $("#input-text").text("Copy and share map data below (Ctrl+C & Enter):");
    $("#input-ok").hide();
    $("#input-val").val(data);
    $("#input-box").show();
    $("#input-val").focus();
    $("#input-val").select();
    $("#input-val").attr("readonly", "readonly");
    $(".prompt-content .copyonclick").show();
    ga('send', 'event', 'map', 'export', undefined, mymaps.length);
  }
}

function openImportMymaps() {
  $("#input-text").text("Paste exported map data (Ctrl+V & Enter):");
  $("#input-val").val("");
  $("#input-ok").show();
  $("#input-box").show();
  $("#input-val").focus();
  $(".prompt-content .copyonclick").hide();
  $("#input-val").attr("readonly", undefined);
}

function importMymaps() {
  var imported = 0;
  try {
    var data = $("#input-val").val().trim();
    var importedMymaps = data ? JSON.parse(b64DecodeUnicode(data)) : {};
    objectForEach(importedMymaps, function(name, value) {
      var overwrite = mymaps[name];
      var msg = overwrite ? "Overwrite " : "Import ";
      if (confirm(msg + name + "?")) {
        if (overwrite) {
          changeMymapsItem(name, name);
        } else {
          addMymapsItem(name, addMapListEntry(name, MAP_MY, true), true);
        }
        mymaps[name] = value;
        imported++;
      }
    });
  } catch (ex) {
    error("Invalid import data");
  }
  if (imported > 0) {
    saveJsonValOpt("wt.mymaps", mymaps);
    ga('send', 'event', 'map', 'import', undefined, imported);
    saveMapList();
  }
  $("#input-box").hide();
}

$("#input-box-close").click(function() {
  $("#input-box").hide();
  return false;
});

$("#input-val").keyup(function(event) {
  if (event.which == 27) {
    $("#input-box").hide();
  } else if (event.keyCode == 13) {
    var isImport = $("#input-ok").is(":visible");
    $("#input-box").hide();
    if (isImport) {
      importMymaps();
    }
  }
});

$("#input-ok").click(importMymaps);

$("#mymaps-import").click(openImportMymaps);
$("#mymaps-export").click(openExportMymaps);

if (!supportsBase64() || !JSON || !JSON.parse || !JSON.stringify) {
  $("#mymaps-import").attr("disabled", "disabled");
  $("#mymaps-export").attr("disabled", "disabled");
}

// ------------------- ready?

$(document).ready(function() {
  showMapsList();
  // ----- drag & drop list -----
  $("#mymaps-list").sortable({
    //scroll: true,
    handle: ".map-drag, .map-name",
    update: function(evt) {
      // get map-item
      var item = $(evt.target).parents(".map-item");
      // NOTE: item may be detached from the list at this step
      // moved item name
      var name = item.find(".map-name").text();
      // get new index
      var toIdx = getMapItem(name).index();
      // get old index
      var fromIdx = getMapListEntryIndex(name);
      if (fromIdx != toIdx) {
        // update list data
        moveMapListEntry(fromIdx, toIdx);
        saveMapList();
      }
    }
  });
});

$(window).on("unload", function() {
  $("#mymaps-list").sortable('destroy');
});
<|endoftext|>


<commit_before>var _               = require('underscore'),
    request         = require('request'),
    querystring     = require('querystring');

var apiBase = 'https://www.googleapis.com/language/translate/v2/';

module.exports = function(apiKey) {

  ////
  //  SEND REQUEST
  ////

  var get  = function(path, data, cb) {
    var url = apiBase + path + '?' + querystring.stringify(_.extend({ 'key': apiKey }, data));
    request.get(url, globalResponseHandler(cb));
  };

  ////
  //   API ENDPOINTS
  ////

  var api = {};

  // TODO send as post if over 5k
  api.translate = function(strings, sourceLang, targetLang, cb) {
    if (typeof strings !== 'string' && !Array.isArray(strings)) return cb('Input source must be a string or array of strings');
    if (typeof sourceLang !== 'string') return cb('No target language specified. Must be a string');

    // Make sourceLang optional
    if (!cb) {
      cb = targetLang;
      targetLang = sourceLang;
      sourceLang = null;
    }
    if (!cb) return console.log('No callback defined');

    // Compile data
    var data = {
      q: strings,
      target: targetLang
    };
    if (sourceLang) data.source = sourceLang;

    get('', data, parseTranslations(cb, strings));
  };

  api.getAvailableLanguages = function(data, cb) {
    // Data param is optional
    if (typeof data === 'function') {
      cb = data;
      data = {};
    }
    if (!cb) return console.log('No callback defined');

    get('languages', data, parseAvailableLanguages(cb));
  };

  // TODO send as post if over 5k
  api.detectSource = function(data, cb) {
    if (!cb) return console.log('No callback defined');
    if (typeof data !== 'string' && !Array.isArray(data)) return cb('Input source must be a string or array of strings');

    get('detect', {q: data}, parseSourceDetections(cb));
  };

  ////
  //   RESPONSE HANDLERS
  ////

  var globalResponseHandler = function(cb) {
    return function(err, res, body) {
      if (!cb) return;

      // Catch connection errors
      if (err || !res) {
        var returnErr = 'Error connecting to Google';
        if (err) returnErr += ': ' + err.code;
        err = returnErr;
      } else if (res.statusCode !== 200) {
        err = 'Something went wrong. Google responded with a ' + res.statusCode;
      }
      if (err) return cb(err, null);

      // Catch parse error
      try {
        body = JSON.parse(body);
      } catch(e) {
        err = 'Could not parse response from Google: ' + body;
        return cb(err, null);
      }

      // Return response
      cb(null, body);
    };
  };

  var parseTranslations = function(cb, originalStrings) {
    return function(err, data) {
      if (err) return cb(err, null);

      // Remove nesting
      data = data.data;
      data = data.translations ? data.translations : data;

      // Add originalText to response
      if (data.length === 1) {
        data = data[0];
      } else {
        originalStrings.forEach(function(s, index){
          if (data[index]) _.extend(data[index], { originalText: s });
        });
      }

      // Return nested languages array
      cb(null, data);
    };
  };

  var parseAvailableLanguages = function(cb) {
    return function(err, data) {
      if (err) return cb(err, null);
      cb(null, data.data.languages);
    }
  };

  var parseSourceDetections = function(cb) {
    return function(err, data) {
      if (err) return cb(err, null);

      // Remove nesting and parse
      data = data.data && data.data.detections ? data.data.detections : data;
      if (data.length > 1) {
        data = data.map(function(d){
          return d[0];
        });
      } else {
        data = data[0][0];
      }

      cb(null, data);
    };
  };

  ////
  //   RETURN API
  ////

  return {
    translate:                api.translate,
    getAvailableLanguages:    api.getAvailableLanguages,
    detectSource:             api.detectSource
  };

};

<commit_msg>Translate very large arrays of strings by splitting them up, making async calls, and merging them once all calls return<commit_after>var _               = require('underscore'),
    request         = require('request'),
    querystring     = require('querystring'),
    async           = require('async');

// TODO Make queries between 2-5k POST instead of GET

var apiBase = 'https://www.googleapis.com/language/translate/v2/',
    maxGetQueryLen = 10;

module.exports = function(apiKey) {

  ////
  //  SEND REQUEST
  ////

  var get  = function(path, data, cb) {
    var url = apiBase + path + '?' + querystring.stringify(_.extend({ 'key': apiKey }, data));
    request.get(url, globalResponseHandler(cb));
  };

  ////
  //   API ENDPOINTS
  ////

  var api = {};

  // TODO send as post if over 5k
  api.translate = function(strings, sourceLang, targetLang, cb) {
    if (typeof strings !== 'string' && !Array.isArray(strings)) return cb('Input source must be a string or array of strings');
    if (typeof sourceLang !== 'string') return cb('No target language specified. Must be a string');

    // Make sourceLang optional
    if (!cb) {
      cb = targetLang;
      targetLang = sourceLang;
      sourceLang = null;
    }
    if (!cb) return console.log('No callback defined');

    // Split into multiple calls if query is longer than allowed by Google
    var queries;
    if (Array.isArray(strings) && encodeURIComponent(strings.join(',')).length > maxGetQueryLen && strings.length !== 1) {
      var stringSets = [];
      splitArraysForGoogle(strings, stringSets);
    } else {
      stringSets = [strings];
    }

    // Compile data
    var data = { target: targetLang };
    if (sourceLang) data.source = sourceLang;

    // Run queries async
    async.map(stringSets, function(stringSet, done) {

      get('', _.extend({ q: stringSet }, data), parseTranslations(stringSet, done));

    }, function(err, translations) {
      if (err) return cb(err);

      // Merge and return translation
      cb(null, _.flatten(translations));
    });

  };

  api.getAvailableLanguages = function(data, cb) {
    // Data param is optional
    if (typeof data === 'function') {
      cb = data;
      data = {};
    }
    if (!cb) return console.log('No callback defined');

    get('languages', data, parseAvailableLanguages(cb));
  };

  // TODO send as post if over 5k
  api.detectSource = function(data, cb) {
    if (!cb) return console.log('No callback defined');
    if (typeof data !== 'string' && !Array.isArray(data)) return cb('Input source must be a string or array of strings');

    get('detect', {q: data}, parseSourceDetections(cb));
  };

  ////
  //   RESPONSE HANDLERS
  ////

  var globalResponseHandler = function(cb) {
    return function(err, res, body) {
      if (!cb) return;

      // Catch connection errors
      if (err || !res) {
        var returnErr = 'Error connecting to Google';
        if (err) returnErr += ': ' + err.code;
        err = returnErr;
      } else if (res.statusCode !== 200) {
        err = 'Something went wrong. Google responded with a ' + res.statusCode;
      }
      if (err) return cb(err, null);

      // Catch parse error
      try {
        body = JSON.parse(body);
      } catch(e) {
        err = 'Could not parse response from Google: ' + body;
        return cb(err, null);
      }

      // Return response
      cb(null, body);
    };
  };

  var parseTranslations = function(originalStrings, done) {
    return function(err, data) {
      if (err) return cb(err, null);

      // Remove nesting
      data = data.data;
      data = data.translations ? data.translations : data;

      // Add originalText to response
      originalStrings.forEach(function(s, index){
        if (data[index]) _.extend(data[index], { originalText: s });
      });

      // Return nested languages array
      done(null, data);
    };
  };

  var parseAvailableLanguages = function(cb) {
    return function(err, data) {
      if (err) return cb(err, null);
      cb(null, data.data.languages);
    }
  };

  var parseSourceDetections = function(cb) {
    return function(err, data) {
      if (err) return cb(err, null);

      // Remove nesting and parse
      data = data.data && data.data.detections ? data.data.detections : data;
      if (data.length > 1) {
        data = data.map(function(d){
          return d[0];
        });
      } else {
        data = data[0][0];
      }

      cb(null, data);
    };
  };

  ////
  //  HELPERS
  ////

  // Return array of arrays that are short enough for Google to handle
  var splitArraysForGoogle = function(arr, result) {
    if (encodeURIComponent(arr.join(',')).length > maxGetQueryLen && arr.length !== 1) {
      var mid = Math.floor(arr.length / 2);
      splitArraysForGoogle(arr.slice(0,mid), result);
      splitArraysForGoogle(arr.slice(mid, arr.length), result);
    } else {
      result.push(arr)
    }
  };

  ////
  //   RETURN API
  ////

  return {
    translate:                api.translate,
    getAvailableLanguages:    api.getAvailableLanguages,
    detectSource:             api.detectSource
  };

};

<|endoftext|>


<commit_before>
angular.module('cesium.map.utils.services', ['cesium.services', 'ui-leaflet'])

.factory('MapUtils', function($timeout, $q, $translate, leafletData, csConfig, csSettings, esGeo, UIUtils, leafletHelpers) {
  'ngInject';

  var
    googleApiKey = csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.googleApiKey,
    constants = {
      locations: {
        FRANCE: {
          lat: 46.5588603, lng: 4.229736328124999, zoom: 6
        }
      },
      LOCALIZE_ZOOM: 15
    },
    data = {
      cache: {}
    };
  constants.DEFAULT_CENTER = csSettings.data && csSettings.data.plugins && csSettings.data.plugins.map && csSettings.data.plugins.map.center || constants.locations.FRANCE;

  function initMap(options){
    options = angular.merge({
      center: angular.copy(constants.DEFAULT_CENTER),
      cache: false,
      defaults: {
        scrollWheelZoom: true
      },
      layers: {
        baselayers: {
          osm: {
            name: 'OpenStreetMap',
            type: 'xyz',
            url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            layerOptions: {
              subdomains: ["a", "b", "c"],
              attribution: "© OpenStreetMap",
              continuousWorld: true
            }
          },
          cycle: {
            name: "Google map",
            type: "xyz",
            url: 'http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key='+googleApiKey,
            layerOptions: {
              subdomains: ['mt0','mt1','mt2','mt3'],
              attribution: "© Google",
              continuousWorld: true
            }
          }
        }
      },
      controls: {
        custom: []
      }
    }, options || {});

    // Restore existing map options
    if (options.cache && data.cache[options.cache]) {
      console.debug("Restoring cache :", data.cache[options.cache]);
      options = angular.merge(options, data.cache[options.cache]);
    }

    // Translate overlays name, if any
    var overlaysNames;
    if (options.layers.overlays) {
      overlaysNames = _.keys(options.layers.overlays).reduce(function (res, key) {
        return res.concat(options.layers.overlays[key].name);
      }, []);

      $translate(overlaysNames).then(function (translations) {
        // Translate overlay names
        _.keys(options.layers.overlays || {}).forEach(function (key) {
          options.layers.overlays[key].name = translations[options.layers.overlays[key].name];
        });
      });
    }

    return options;
  }

  function updateMapCenter(map, center) {
    if (isSameCenter(center, map)) return $q.when();

    return $timeout(function () {
      map.invalidateSize();
      map._resetView(center, center.zoom, true);
    }, 300);
  }

  function getCenter(options) {
    if (!options) return;
    var center;
    if (options.lat) {
      center = {};
      center.lat = parseFloat(options.lat);
    }
    if (options.lng || options.lon) {
      center = center || {};
      center.lng = parseFloat(options.lng || options.lon);
    }
    if (options.zoom) {
      center = center || {};
      center.zoom = parseFloat(options.zoom);
    }
    if (!center) return;

    // If missing some properties, complete with defaults
    if (!leafletHelpers.isValidCenter(center)) {
      center = angular.merge({}, constants.DEFAULT_CENTER, center);
    }
    return center;
  }

  function isSameCenter(center, map) {
    return leafletHelpers.isSameCenterOnMap(center, map);
  }

  function isDefaultCenter(centerModel) {
    var mapCenter = constants.DEFAULT_CENTER;
    if (centerModel.lat && centerModel.lng && mapCenter.lat.toFixed(4) === centerModel.lat.toFixed(4) && mapCenter.lng.toFixed(4) === centerModel.lng.toFixed(4) && mapCenter.zoom === centerModel.zoom) {
      return true;
    }
    return false;
  }

  function saveMapOptions(options) {
    if (options.cache) {
      data.cache[options.cache] = {
        center: options.center,
        bounds: options.bounds,
        layers: {
          baselayers: options.layers.baselayers
        }
      };
    }
  }

  // Create a default serach control, with default options
  function initSearchControl(options) {

    options = options || {};
    options.initial = angular.isDefined(options.initial) ? options.initial : false;
    options.marker = angular.isDefined(options.marker) ? options.marker : false;
    options.propertyName = angular.isDefined(options.propertyName) ? options.propertyName : 'title';
    options.position = angular.isDefined(options.position) ? options.position : 'topleft';
    options.zoom = angular.isDefined(options.zoom) ? options.zoom : constants.LOCALIZE_ZOOM;
    options.markerLocation = angular.isDefined(options.markerLocation) ? options.markerLocation : true;

    var translatePromise = $translate(['MAP.COMMON.SEARCH_DOTS', 'COMMON.SEARCH_NO_RESULT']);

    return {
      // Simulate an addTo function, but wait for end of translations job
      addTo: function (map) {
        translatePromise.then(function (translations) {
          L.control.search(angular.merge(options, {
            textPlaceholder: translations['MAP.COMMON.SEARCH_DOTS'],
            textErr: translations['COMMON.SEARCH_NO_RESULT']
          })).addTo(map);
        });
      }
    };
  }

  function initLocalizeMeControl() {
    return L.easyButton('icon ion-android-locate', function(btn, map){
      return esGeo.point.current()
        .then(function(res) {
          map.invalidateSize();
          map._resetView({
            lat: res.lat,
            lng: res.lon
          }, constants.LOCALIZE_ZOOM, true);
        })
        .catch(UIUtils.onError('MAP.ERROR.LOCALIZE_ME_FAILED'));
    });
  }

  return {
    map: initMap,
    center: {
      get: getCenter,
      isSame: isSameCenter,
      isDefault: isDefaultCenter
    },
    updateCenter: updateMapCenter,
    control: {
      search: initSearchControl,
      localizeMe: initLocalizeMeControl
    },
    cache: {
      save: saveMapOptions
    },
    constants: constants
  };

});
<commit_msg>[fix] Map: force SSL map services, when using HTTPS<commit_after>
angular.module('cesium.map.utils.services', ['cesium.services', 'ui-leaflet'])

.factory('MapUtils', function($timeout, $q, $translate, $window, leafletData, csConfig, csSettings, esGeo, UIUtils, leafletHelpers) {
  'ngInject';

  var
    googleApiKey = csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.googleApiKey,
    isHttps = ($window.location.protocol === 'https:'),
    constants = {
      locations: {
        FRANCE: {
          lat: 46.5588603, lng: 4.229736328124999, zoom: 6
        }
      },
      LOCALIZE_ZOOM: 15
    },
    data = {
      cache: {}
    };
  constants.DEFAULT_CENTER = csSettings.data && csSettings.data.plugins && csSettings.data.plugins.map && csSettings.data.plugins.map.center || constants.locations.FRANCE;

  function initMap(options){
    options = angular.merge({
      center: angular.copy(constants.DEFAULT_CENTER),
      cache: false,
      defaults: {
        scrollWheelZoom: true
      },
      layers: {
        baselayers: {
          osm: {
            name: 'OpenStreetMap',
            type: 'xyz',
            url: (isHttps ? 'https' : 'http' ) + '://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            layerOptions: {
              subdomains: ["a", "b", "c"],
              attribution: "© OpenStreetMap",
              continuousWorld: true
            }
          },
          cycle: {
            name: "Google map",
            type: "xyz",
            url: (isHttps ? 'https' : 'http' ) + '://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}&key='+googleApiKey,
            layerOptions: {
              subdomains: ['mt0','mt1','mt2','mt3'],
              attribution: "© Google",
              continuousWorld: true
            }
          }
        }
      },
      controls: {
        custom: []
      }
    }, options || {});

    // Restore existing map options
    if (options.cache && data.cache[options.cache]) {
      console.debug("Restoring cache :", data.cache[options.cache]);
      options = angular.merge(options, data.cache[options.cache]);
    }

    // Translate overlays name, if any
    var overlaysNames;
    if (options.layers.overlays) {
      overlaysNames = _.keys(options.layers.overlays).reduce(function (res, key) {
        return res.concat(options.layers.overlays[key].name);
      }, []);

      $translate(overlaysNames).then(function (translations) {
        // Translate overlay names
        _.keys(options.layers.overlays || {}).forEach(function (key) {
          options.layers.overlays[key].name = translations[options.layers.overlays[key].name];
        });
      });
    }

    return options;
  }

  function updateMapCenter(map, center) {
    if (isSameCenter(center, map)) return $q.when();

    return $timeout(function () {
      map.invalidateSize();
      map._resetView(center, center.zoom, true);
    }, 300);
  }

  function getCenter(options) {
    if (!options) return;
    var center;
    if (options.lat) {
      center = {};
      center.lat = parseFloat(options.lat);
    }
    if (options.lng || options.lon) {
      center = center || {};
      center.lng = parseFloat(options.lng || options.lon);
    }
    if (options.zoom) {
      center = center || {};
      center.zoom = parseFloat(options.zoom);
    }
    if (!center) return;

    // If missing some properties, complete with defaults
    if (!leafletHelpers.isValidCenter(center)) {
      center = angular.merge({}, constants.DEFAULT_CENTER, center);
    }
    return center;
  }

  function isSameCenter(center, map) {
    return leafletHelpers.isSameCenterOnMap(center, map);
  }

  function isDefaultCenter(centerModel) {
    var mapCenter = constants.DEFAULT_CENTER;
    if (centerModel.lat && centerModel.lng && mapCenter.lat.toFixed(4) === centerModel.lat.toFixed(4) && mapCenter.lng.toFixed(4) === centerModel.lng.toFixed(4) && mapCenter.zoom === centerModel.zoom) {
      return true;
    }
    return false;
  }

  function saveMapOptions(options) {
    if (options.cache) {
      data.cache[options.cache] = {
        center: options.center,
        bounds: options.bounds,
        layers: {
          baselayers: options.layers.baselayers
        }
      };
    }
  }

  // Create a default serach control, with default options
  function initSearchControl(options) {

    options = options || {};
    options.initial = angular.isDefined(options.initial) ? options.initial : false;
    options.marker = angular.isDefined(options.marker) ? options.marker : false;
    options.propertyName = angular.isDefined(options.propertyName) ? options.propertyName : 'title';
    options.position = angular.isDefined(options.position) ? options.position : 'topleft';
    options.zoom = angular.isDefined(options.zoom) ? options.zoom : constants.LOCALIZE_ZOOM;
    options.markerLocation = angular.isDefined(options.markerLocation) ? options.markerLocation : true;

    var translatePromise = $translate(['MAP.COMMON.SEARCH_DOTS', 'COMMON.SEARCH_NO_RESULT']);

    return {
      // Simulate an addTo function, but wait for end of translations job
      addTo: function (map) {
        translatePromise.then(function (translations) {
          L.control.search(angular.merge(options, {
            textPlaceholder: translations['MAP.COMMON.SEARCH_DOTS'],
            textErr: translations['COMMON.SEARCH_NO_RESULT']
          })).addTo(map);
        });
      }
    };
  }

  function initLocalizeMeControl() {
    return L.easyButton('icon ion-android-locate', function(btn, map){
      return esGeo.point.current()
        .then(function(res) {
          map.invalidateSize();
          map._resetView({
            lat: res.lat,
            lng: res.lon
          }, constants.LOCALIZE_ZOOM, true);
        })
        .catch(UIUtils.onError('MAP.ERROR.LOCALIZE_ME_FAILED'));
    });
  }

  return {
    map: initMap,
    center: {
      get: getCenter,
      isSame: isSameCenter,
      isDefault: isDefaultCenter
    },
    updateCenter: updateMapCenter,
    control: {
      search: initSearchControl,
      localizeMe: initLocalizeMeControl
    },
    cache: {
      save: saveMapOptions
    },
    constants: constants
  };

});
<|endoftext|>


<commit_before>var round = function(num)
{
  return (num + 0.5) | 0
}
var floor = function(num)
{
  return num | 0
}
var ceil = function(num)
{
  return (num | 0) == num ? num | 0 : (num + 1) | 0
}
var abs = Math.abs
var sqrt = Math.sqrt

var log = function(num)
{
  var result = Math.log(num)

  return result
}

var signed_log = function(num)
{
  var sign = num < 0 ? -1 : 1;

  var result = Math.log(abs(num))

  return sign * result
}

var max = function(a, b)
{
  return (a > b) ? a : b;
}

var min = function(a, b)
{
  return (a < b) ? a : b;
}

var fix_all = function(old_all)
{
  var new_all = []
  for (var obj in old_all)
  {
    if (old_all[obj] != undefined)
    {
      new_all.push(old_all[obj])
    }
  }
  return new_all;
}

var count_object_keys = function(obj)
{
  var result = 0;
  for (var prop in obj)
  {
    if (obj.hasOwnProperty(prop))
      result++
  }

  return result
}
<commit_msg>Add a familiar warn to quickly access console.log<commit_after>var round = function(num)
{
  return (num + 0.5) | 0
}
var floor = function(num)
{
  return num | 0
}
var ceil = function(num)
{
  return (num | 0) == num ? num | 0 : (num + 1) | 0
}
var abs = Math.abs
var sqrt = Math.sqrt

var log = function(num)
{
  var result = Math.log(num)

  return result
}

var signed_log = function(num)
{
  var sign = num < 0 ? -1 : 1;

  var result = Math.log(abs(num))

  return sign * result
}

var max = function(a, b)
{
  return (a > b) ? a : b;
}

var min = function(a, b)
{
  return (a < b) ? a : b;
}

var fix_all = function(old_all)
{
  var new_all = []
  for (var obj in old_all)
  {
    if (old_all[obj] != undefined)
    {
      new_all.push(old_all[obj])
    }
  }
  return new_all;
}

var count_object_keys = function(obj)
{
  var result = 0;
  for (var prop in obj)
  {
    if (obj.hasOwnProperty(prop))
      result++
  }

  return result
}

var warn = function()
{
  console.log.apply(console, arguments)
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2017 The Android Open Source Project
 *
 * 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.
 */

'use strict';

const REPO_URL = 'https://android.googlesource.com/platform/external/perfetto/';
const GERRIT_REVIEW_URL = 'https://android-review.googlesource.com/c/platform/external/perfetto';
const CHANGES_URL = '/changes/?q=project:platform/external/perfetto+-age:7days&o=DETAILED_ACCOUNTS';
const REPO = 'catapult-project/perfetto';

let botIndex = {};

// Builds a map of bot name -> column index, e.g.:
// {'linux-clang-x86_64-relese' -> 1, 'android-clang-arm-debug' -> 2}.
function GetColumnIndexes() {
  const cols = document.getElementById('cls_header').children;
  for (let i = 0; i < cols.length; i++) {
    const id = cols[i].id;
    if (id)
      botIndex[id] = i + 4 /* 4 = subject...updated columns */;
  }
}

function GetTravisStatusForJob(jobId, div) {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState != 4 || this.status != 200)
      return;
    let resp = JSON.parse(this.responseText);
    let jobName = resp.config.env.split(' ')[0];
    if (jobName.startsWith('CFG='))
      jobName = jobName.substring(4);
    let link = document.createElement('a');
    link.href = 'https://travis-ci.org/' + REPO + '/jobs/' + jobId;
    link.title = resp.state + ' [' + jobName + ']';
    link.classList.add(resp.state);
    if (resp.state == 'finished')
      link.innerHTML = 'check_circle';
    else if (resp.state == 'created')
      link.innerHTML = 'autorenew';
    else if (resp.state == 'errored' || resp.state == 'cancelled')
      link.innerHTML = 'bug_report';
    else
      link.innerHTML = 'hourglass_full';
    let td = div.children[botIndex[jobName]];
    td.innerHTML = '';
    td.appendChild(link);
  };
  xhr.open('GET', 'https://api.travis-ci.org/jobs/' + jobId, true);
  xhr.send();
}

function GetTravisStatusForBranch(branch, div) {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 404) {
      return;
    }
    if (this.readyState != 4 || this.status != 200)
      return;
    let resp = JSON.parse(this.responseText);
    for (const jobId of resp.branch.job_ids)
      GetTravisStatusForJob(jobId, div);
  };
  let url = ('https://api.travis-ci.org/repos/' + REPO + '/branches/' + branch);
  xhr.open('GET', url, true);
  xhr.send();
}

function CreateRowForBranch(branch, href, subject, status, author, updated) {
    let table = document.getElementById('cls');
    let tr = document.createElement('tr');

    let link = document.createElement('a');
    link.href = href;
    link.innerText = subject;
    let td = document.createElement('td');
    td.appendChild(link);
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = status;
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = author;
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = updated;
    tr.appendChild(td);

    for (let _ in botIndex) {
      td = document.createElement('td');
      td.classList.add('job');
      tr.appendChild(td);
    }
    table.appendChild(tr);
    GetTravisStatusForBranch(branch, tr);
}

function LoadGerritCLs() {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState != 4 || this.status != 200)
      return;
    let json = this.responseText;
    if (json.startsWith(')]}\''))
      json = json.substring(4);
    let resp = JSON.parse(json);
    for (const cl of resp) {
      const branch = 'changes/' + cl._number;
      const href = GERRIT_REVIEW_URL + '/+/' + cl._number;;
      const lastUpdate = new Date(cl.updated + ' UTC');
      const lastUpdateMins = Math.ceil((Date.now() - lastUpdate) / 60000);
      let lastUpdateText = '';
      if (lastUpdateMins < 60)
        lastUpdateText = lastUpdateMins + ' mins ago';
      else if (lastUpdateMins < 60 * 24)
        lastUpdateText = Math.ceil(lastUpdateMins / 60) + ' hours ago';
      else
        lastUpdateText = lastUpdate.toLocaleDateString();
      CreateRowForBranch(branch, href, cl.subject, cl.status,
          cl.owner.email.replace('@google.com', '@'), lastUpdateText);
    }
  };
  xhr.open('GET', CHANGES_URL, true);
  xhr.send();
}

GetColumnIndexes();
CreateRowForBranch('master', REPO_URL, '*** master branch ***', 'MASTER', '', '');
LoadGerritCLs();
<commit_msg>Fix travis-ci.appspost.com, report failed builds as such am: f07c859525 am: 3199739b25<commit_after>/**
 * Copyright (c) 2017 The Android Open Source Project
 *
 * 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.
 */

'use strict';

const REPO_URL = 'https://android.googlesource.com/platform/external/perfetto/';
const GERRIT_REVIEW_URL = 'https://android-review.googlesource.com/c/platform/external/perfetto';
const CHANGES_URL = '/changes/?q=project:platform/external/perfetto+-age:7days&o=DETAILED_ACCOUNTS';
const REPO = 'catapult-project/perfetto';

let botIndex = {};

// Builds a map of bot name -> column index, e.g.:
// {'linux-clang-x86_64-relese' -> 1, 'android-clang-arm-debug' -> 2}.
function GetColumnIndexes() {
  const cols = document.getElementById('cls_header').children;
  for (let i = 0; i < cols.length; i++) {
    const id = cols[i].id;
    if (id)
      botIndex[id] = i + 4 /* 4 = subject...updated columns */;
  }
}

function GetTravisStatusForJob(jobId, div) {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState != 4 || this.status != 200)
      return;
    let resp = JSON.parse(this.responseText);
    let jobName = resp.config.env.split(' ')[0];
    if (jobName.startsWith('CFG='))
      jobName = jobName.substring(4);
    let link = document.createElement('a');
    link.href = 'https://travis-ci.org/' + REPO + '/jobs/' + jobId;
    link.title = resp.state + ' [' + jobName + ']';
    let jobState = resp.state;
    if (resp.state == 'finished' && resp.result !== 0)
      jobState = 'errored';
    link.classList.add(jobState);
    if (jobState == 'finished')
      link.innerHTML = 'check_circle';
    else if (jobState == 'created')
      link.innerHTML = 'autorenew';
    else if (jobState == 'errored' || jobState == 'cancelled')
      link.innerHTML = 'bug_report';
    else
      link.innerHTML = 'hourglass_full';
    let td = div.children[botIndex[jobName]];
    td.innerHTML = '';
    td.appendChild(link);
  };
  xhr.open('GET', 'https://api.travis-ci.org/jobs/' + jobId, true);
  xhr.send();
}

function GetTravisStatusForBranch(branch, div) {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 404) {
      return;
    }
    if (this.readyState != 4 || this.status != 200)
      return;
    let resp = JSON.parse(this.responseText);
    for (const jobId of resp.branch.job_ids)
      GetTravisStatusForJob(jobId, div);
  };
  let url = ('https://api.travis-ci.org/repos/' + REPO + '/branches/' + branch);
  xhr.open('GET', url, true);
  xhr.send();
}

function CreateRowForBranch(branch, href, subject, status, author, updated) {
    let table = document.getElementById('cls');
    let tr = document.createElement('tr');

    let link = document.createElement('a');
    link.href = href;
    link.innerText = subject;
    let td = document.createElement('td');
    td.appendChild(link);
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = status;
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = author;
    tr.appendChild(td);

    td = document.createElement('td');
    td.innerText = updated;
    tr.appendChild(td);

    for (let _ in botIndex) {
      td = document.createElement('td');
      td.classList.add('job');
      tr.appendChild(td);
    }
    table.appendChild(tr);
    GetTravisStatusForBranch(branch, tr);
}

function LoadGerritCLs() {
  let xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (this.readyState != 4 || this.status != 200)
      return;
    let json = this.responseText;
    if (json.startsWith(')]}\''))
      json = json.substring(4);
    let resp = JSON.parse(json);
    for (const cl of resp) {
      const branch = 'changes/' + cl._number;
      const href = GERRIT_REVIEW_URL + '/+/' + cl._number;;
      const lastUpdate = new Date(cl.updated + ' UTC');
      const lastUpdateMins = Math.ceil((Date.now() - lastUpdate) / 60000);
      let lastUpdateText = '';
      if (lastUpdateMins < 60)
        lastUpdateText = lastUpdateMins + ' mins ago';
      else if (lastUpdateMins < 60 * 24)
        lastUpdateText = Math.ceil(lastUpdateMins / 60) + ' hours ago';
      else
        lastUpdateText = lastUpdate.toLocaleDateString();
      CreateRowForBranch(branch, href, cl.subject, cl.status,
          cl.owner.email.replace('@google.com', '@'), lastUpdateText);
    }
  };
  xhr.open('GET', CHANGES_URL, true);
  xhr.send();
}

GetColumnIndexes();
CreateRowForBranch('master', REPO_URL, '*** master branch ***', 'MASTER', '', '');
LoadGerritCLs();
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';
import packageConfig from './config-schema.json';

export default {
	config: packageConfig,
	events: null,
	timer1: null,

	activate(state) {
		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.workspace.observeTextEditors(this.callback.bind(this))
		);
		this.subscriptions.add(
			atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this))
		);
	},

	callback(editor) {
		var changeNonPlainText = atom.config.get('shebang-set-grammar.changeNonPlainText');
		if (! changeNonPlainText) {
			if (editor.getGrammar() != atom.grammars.nullGrammar) {
				return;
			}
		}

		var bin_name = this.read_shebang(editor);
		if (bin_name == null) {
			return;
		}

		var aliases = atom.config.get('shebang-set-grammar.aliases');
		try {
			aliases = JSON.parse(aliases);
		}
		catch(err) {
			console.error("Inavlid \"aliases\" setting value: "+err);
			aliases = {};
		}

		if (bin_name in aliases) {
			bin_name = aliases[bin_name];
		}

		grammar = atom.grammars.grammarForScopeName('source.'+bin_name);
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	read_shebang(editor) {
		/*
			#! /usr/bin/python 	=> python
			#! /usr/bin/python -e 	=> python
			#! /bin/bash 			=> bash
			#! perl -e 			=> perl
		*/

		var line = editor.lineTextForBufferRow(0);
		if (! line.startsWith('#!')) {
			return null;
		}

		line = line.substr(2).trim();

		var parts = line.split(/[\s\t]+/);
		for (var i=parts.length-1; i>0; i--) {
			if (parts[i].startsWith("-")) {
				continue;
			}

			return parts[i];
		}

		parts = parts[0].split("/");
		return parts[parts.length - 1];
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.events != null) {
			this.events.dispose();
			this.events = null;
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<commit_msg>Code refactoring: isolate read_shebang function to not require any Atom resources so it can be easily unit tested<commit_after>'use babel';

import { CompositeDisposable } from 'atom';
import packageConfig from './config-schema.json';

export default {
	config: packageConfig,
	events: null,
	timer1: null,

	activate(state) {
		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.workspace.observeTextEditors(this.callback.bind(this))
		);
		this.subscriptions.add(
			atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this))
		);
	},

	callback(editor) {
		var changeNonPlainText = atom.config.get('shebang-set-grammar.changeNonPlainText');
		if (! changeNonPlainText) {
			if (editor.getGrammar() != atom.grammars.nullGrammar) {
				return;
			}
		}

		var bin_name = this.read_shebang(editor.lineTextForBufferRow(0));
		if (bin_name == null) {
			return;
		}

		var aliases = atom.config.get('shebang-set-grammar.aliases');
		try {
			aliases = JSON.parse(aliases);
		}
		catch(err) {
			console.error("Inavlid \"aliases\" setting value: "+err);
			aliases = {};
		}

		if (bin_name in aliases) {
			bin_name = aliases[bin_name];
		}

		grammar = atom.grammars.grammarForScopeName('source.'+bin_name);
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	read_shebang(line) {
		/*
			#! /usr/bin/python 	=> python
			#! /usr/bin/python -e 	=> python
			#! /bin/bash 			=> bash
			#! perl -e 			=> perl
		*/

		if (! line.startsWith('#!')) {
			return null;
		}

		line = line.substr(2).trim();

		var parts = line.split(/[\s\t]+/);
		for (var i=parts.length-1; i>0; i--) {
			if (parts[i].startsWith("-")) {
				continue;
			}

			return parts[i];
		}

		parts = parts[0].split("/");
		return parts[parts.length - 1];
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.events != null) {
			this.events.dispose();
			this.events = null;
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<|endoftext|>


<commit_before>// Define keyboard shortcuts for showing and hiding a custom panel.
var { Hotkey } = require("hotkeys");

var showHotKey = Hotkey({
  combo: "accel-meta-q",
  onPress: function() {
    console.log("We disbled Meta-Q. You are welcome.");
  }
});
<commit_msg>Change behavior to quit after two Meta-Q presses in under 1s.<commit_after>// Define keyboard shortcuts for showing and hiding a custom panel.
var { Cc, Ci } = require("chrome");
var { Hotkey } = require("hotkeys");
var notifications = require("notifications");

var lastQPress = 0;

var showHotKey = Hotkey({
  combo: "accel-meta-q",
  onPress: function() {
    // TODO: Make delay configurable
    if (lastQPress && (Date.now() - lastQPress) < 1000) {
        Cc['@mozilla.org/toolkit/app-startup;1']
            .getService(Ci.nsIAppStartup)
            .quit(Ci.nsIAppStartup.eAttemptQuit)
    }
    else {
        notifications.notify({
            text: "Press Meta-Q again to quit"
        });
    }
    lastQPress = Date.now();
  }
});
<|endoftext|>


<commit_before>/* Copyright 2011 Ask Bjørn Hansen, see LICENSE */
"use strict";

var https = require('https');
var querystring = require('querystring');

function setup_response_handler(req, callback) {
    if (typeof callback !== "function") {
        //console.log("missing callback");
        return;
    }
    req.on('response',
        function(res) {
            var response = '';
            res.setEncoding('utf8');
            res.on('data',
                function(chunk) {
                    response += chunk;
            });
            res.on('end',
                function() {
                    var err = 200 == res.statusCode ? 0 : res.statusCode;
                    try {
                        response = JSON.parse(response);
                    }
                    catch(e) {
                        err = 1;
                        response = { error : { message : "Invalid JSON from stripe.com" } };
                    }
                    callback(err, response)
            });
        });
}

module.exports = function (api_key, options) {
    var defaults = options || {};

    var auth = 'Basic ' + new Buffer(api_key + ":").toString('base64');

    function _request(method, path, data, callback) {

        //console.log("data", typeof data, data);

        // convert first level of deep data structures to foo[bar]=baz syntax
        Object.keys(data).forEach(function(key) {
            if (typeof data[key] === 'object' && data[key] !== null) {
                var o = data[key];
                delete data[key];
                Object.keys(o).forEach(function(k) {
                    var new_key = key + "[" + k + "]";
                    data[new_key] = o[k];
                });
            }
        });

        var request_data = querystring.stringify(data);

        //console.log(method, "request for", path);
        //console.log("http request", request_data);

        var request_options = {
              host: 'api.stripe.com',
              port: '443',
              path: path,
              method: method,
              headers: {
                  'Authorization': auth,
                  'Accept': 'application/json',
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'Content-Length': request_data.length
              }
          };

        var req = https.request(request_options);
        setup_response_handler(req, callback);
        req.write(request_data);
        req.end();
    }

    function post(path, data, callback) {
        _request('POST', path, data, callback);
    }

    function get(path, data, callback) {
        _request('GET', path, data, callback);
    }

    function del(path, data, callback) {
        _request('DELETE', path, data, callback);
    }

    return {
        charges: {
            create: function (data, cb) {
                post("/v1/charges", data, cb);
            },
            retrieve: function(charge_id, cb) {
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                get("/v1/charges/" + charge_id, {}, cb);
            },
            refund: function(charge_id, amount, cb) {
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                post("/v1/charges/" + charge_id + "/refund", { amount: amount }, cb);
            },
            list: function(data, cb) {
                get("/v1/charges", data, cb);
            },
        },
        customers: {
            create: function (data, cb) {
                post("/v1/customers", data, cb);
            },
            retrieve: function(customer_id, cb) {
                if (!(customer_id && typeof customer_id === 'string')) {
                    cb("customer_id required");
                }
                get("/v1/customers/" + customer_id, {}, cb);
            },
            update: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id, data, cb);
            },
            del: function(customer_id, cb) {
                del("/v1/customers/" + customer_id, {}, cb);
            },
            list: function(count, offset, cb) {
                get("/v1/customers", { count: count, offset: offset}, cb );
            }
        },
        token: {
            create: function (data, cb) {
                post("/v1/tokens", data, cb)
            },
            retrieve: function (token_id, cb) {
                get("/v1/tokens/" + token_id, cb)
            }
        },
    };
}
<commit_msg>Amount in stripe.charges.refund is optional.<commit_after>/* Copyright 2011 Ask Bjørn Hansen, see LICENSE */
"use strict";

var https = require('https');
var querystring = require('querystring');

function setup_response_handler(req, callback) {
    if (typeof callback !== "function") {
        //console.log("missing callback");
        return;
    }
    req.on('response',
        function(res) {
            var response = '';
            res.setEncoding('utf8');
            res.on('data',
                function(chunk) {
                    response += chunk;
            });
            res.on('end',
                function() {
                    var err = 200 == res.statusCode ? 0 : res.statusCode;
                    try {
                        response = JSON.parse(response);
                    }
                    catch(e) {
                        err = 1;
                        response = { error : { message : "Invalid JSON from stripe.com" } };
                    }
                    callback(err, response)
            });
        });
}

module.exports = function (api_key, options) {
    var defaults = options || {};

    var auth = 'Basic ' + new Buffer(api_key + ":").toString('base64');

    function _request(method, path, data, callback) {

        //console.log("data", typeof data, data);

        // convert first level of deep data structures to foo[bar]=baz syntax
        Object.keys(data).forEach(function(key) {
            if (typeof data[key] === 'object' && data[key] !== null) {
                var o = data[key];
                delete data[key];
                Object.keys(o).forEach(function(k) {
                    var new_key = key + "[" + k + "]";
                    data[new_key] = o[k];
                });
            }
        });

        var request_data = querystring.stringify(data);

        //console.log(method, "request for", path);
        //console.log("http request", request_data);

        var request_options = {
              host: 'api.stripe.com',
              port: '443',
              path: path,
              method: method,
              headers: {
                  'Authorization': auth,
                  'Accept': 'application/json',
                  'Content-Type': 'application/x-www-form-urlencoded',
                  'Content-Length': request_data.length
              }
          };

        var req = https.request(request_options);
        setup_response_handler(req, callback);
        req.write(request_data);
        req.end();
    }

    function post(path, data, callback) {
        _request('POST', path, data, callback);
    }

    function get(path, data, callback) {
        _request('GET', path, data, callback);
    }

    function del(path, data, callback) {
        _request('DELETE', path, data, callback);
    }

    return {
        charges: {
            create: function (data, cb) {
                post("/v1/charges", data, cb);
            },
            retrieve: function(charge_id, cb) {
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                get("/v1/charges/" + charge_id, {}, cb);
            },
            refund: function(charge_id, amount, cb) {
                var requestParams = {};
                if(!(charge_id && typeof charge_id === 'string')) {
                    cb("charge_id required");
                }
                if (typeof amount === 'function') {
                    cb = amount;
                    amount = null;
                }
                if (amount) {
                    requestParams.amount = amount;
                }
                post("/v1/charges/" + charge_id + "/refund", requestParams, cb);
            },
            list: function(data, cb) {
                get("/v1/charges", data, cb);
            },
        },
        customers: {
            create: function (data, cb) {
                post("/v1/customers", data, cb);
            },
            retrieve: function(customer_id, cb) {
                if (!(customer_id && typeof customer_id === 'string')) {
                    cb("customer_id required");
                }
                get("/v1/customers/" + customer_id, {}, cb);
            },
            update: function(customer_id, data, cb) {
                post("/v1/customers/" + customer_id, data, cb);
            },
            del: function(customer_id, cb) {
                del("/v1/customers/" + customer_id, {}, cb);
            },
            list: function(count, offset, cb) {
                get("/v1/customers", { count: count, offset: offset}, cb );
            }
        },
        token: {
            create: function (data, cb) {
                post("/v1/tokens", data, cb)
            },
            retrieve: function (token_id, cb) {
                get("/v1/tokens/" + token_id, cb)
            }
        },
    };
}
<|endoftext|>