<commit_before>//---------------------------------------------------------
// Utils
//---------------------------------------------------------

var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
                "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

function reactFactory(obj) {
  return React.createFactory(React.createClass(obj));
}

function extend(dest, src) {
  for(var key in src) {
    if(!src.hasOwnProperty(key)) { continue; }
    dest[key] = src[key];
  }
  return dest;
}

function range(from, to) {
  if(to === undefined) {
    to = from;
    from = 0;
  }
  var results = [];
  for(var i = from; i < to; i++) {
    results.push(i);
  }
  return results;
}

//---------------------------------------------------------
// UI state
//---------------------------------------------------------

var uiState = {};
var ixer = new Indexing.Indexer();

//---------------------------------------------------------
// Root component
//---------------------------------------------------------

var root = reactFactory({
  displayName: "root",
  render: function() {
    return stage({parent: "body"});
  }
});

//---------------------------------------------------------
// Grid components
//---------------------------------------------------------

var tiles = {};

tiles.debug = {
  flippable: false,
  navigable: false,
  content: reactFactory({
    displayName: "debug-tile",
    render: function() {
      return JSML(["span", "hello, world!"]);
    }
  })
};

var gridTile = reactFactory({
  displayName: "grid-tile",
  render: function() {
    var tile = tiles[this.props.type];
    if(!tile) { throw new Error("Invalid tile type specified: '" + this.props.type + "'."); }

    var style = {
      top: this.props.top,
      left: this.props.left,
      width: this.props.width,
      height: this.props.height
    };

    return JSML(["div", {className: "grid-tile " + this.props.type, style: style}, tile.content(tile)]);
  }
});

var stage = reactFactory({
  displayName: "stage",
  getInitialState: function() {
    return {
      grid: Grid.makeGrid({container: this.props.parent, gutter: 8}),
      tiles: [
        {pos: [0, 0], size: [3, 1], type: "debug"},
        {pos: [3, 0], size: [9, 1], type: "debug"},
        {pos: [0, 1], size: [1, 5], type: "debug"},
        {pos: [1, 1], size: [2, 1], type: "debug"},
        {pos: [3, 1], size: [9, 1], type: "debug"},
        {pos: [1, 2], size: [4, 8], type: "table"},
        {pos: [5, 2], size: [7, 4], type: "table"}
      ]
    };
  },
  render: function() {
    var children = [];
    for(var tileIx = 0, tilesLength = this.state.tiles.length; tileIx < tilesLength; tileIx++) {
      var tileRaw = this.state.tiles[tileIx];
      var tileRect = Grid.getRect(this.state.grid, tileRaw.pos, tileRaw.size);
      var tile = extend(extend({}, tileRaw), tileRect);
      children.push(gridTile(tile));
    }
    var content = ["div", {className: "tile-grid"}];
    content.push.apply(content, children);
    return JSML(content);
  }
});

//---------------------------------------------------------
// Table components
//---------------------------------------------------------

var editable = reactFactory({
  displayName: "editable",
  getInitialState: function() {
    return {value: "", modified: false};
  },
  handleChange: function(e) {
    this.setState({value: e.target.textContent, modified: true});
  },
  handleKeys: function(e) {
    //submit on enter
    if(e.keyCode === 13) {
      this.submit();
      e.preventDefault();
    }
    if(this.props.onKey) {
      this.props.onKey(e);
    }
  },
  submit: function() {
    if(this.props.onSubmit && this.state.modified) {
      this.setState({modified: false});
      this.props.onSubmit(this.state.value);
    }
  },
  render: function() {
    var value = this.state.value || this.props.value;
    return JSML(["div", {"contentEditable": true,
                         "onInput": this.handleChange,
                         "onBlur": this.submit,
                         "onKeyDown": this.handleKeys,
                         dangerouslySetInnerHTML: {__html: value !== undefined ? value : ""}}]);
  }
});

var tableHeader = reactFactory({
  displayName: "tableHeader",
  renameHeader: function(value) {
    //do stuff
    dispatch("rename", {id: this.props.id, value: value});
  },
  render: function() {
    return JSML(["th", editable({value: this.props.field, onSubmit: this.renameHeader})]);
  }
});

var tableRow = reactFactory({
  displayName: "tableRow",
  getInitialState: function() {
    var row = [];
    var cur = this.props.row;
    for(var i = 0, len = this.props.length; i < len; i++) {
      row[i] = cur[i];
    }
    return {row: row};
  },
  componentDidUpdate: function(prev) {
    if(!Indexing.arraysIdentical(prev.row, this.props.row)) {
      var row = this.state.row;
      var cur = this.props.row;
      for(var i = 0, len = this.props.length; i < len; i++) {
        row[i] = cur[i];
      }
    }
  },
  checkRowComplete: function() {
    var row = this.state.row;
    for(var i = 0, len = this.props.length; i < len; i++) {
      if(row[i] === undefined) return false;
    }
    return true;
  },
  setColumn: function(ix, value) {
    this.state.row[ix] = value;
    if(this.checkRowComplete()) {
      this.submitRow();
    } else {
      if(this.props.onRowModified) this.props.onRowModified(this.props.id);
    }
  },
  submitRow: function() {
    if(this.props.isNewRow) {
      if(this.props.onRowAdded) this.props.onRowAdded(this.props.id);
      dispatch("addRow", {table: this.props.table, neue: this.state.row});
    } else {
      dispatch("swapRow", {table: this.props.table, old: this.props.row, neue: this.state.row});
    }
  },
  render: function() {
    var self = this;
    var fields = range(this.props.length).map(function(cur) {
      var content = self.props.row[cur];
      if(content === undefined) {
        content = "";
      }
      if(self.props.editable) {
        return ["td", editable({value: content, onSubmit: function(value) {
          self.setColumn(cur, value);
        }})];
      } else {
        return ["td", content];
      }
    });
    return JSML(["tr", fields]);
  }
});

tiles.table = {
  content: reactFactory({
    displayName: "table",
    getInitialState: function() {
      var table = "foo"; //@FIXME derive this from tableTile index.
      return {table: table, partialRows: [uuid()]};
    },
    rowAdded: function(id) {
      this.setState({partialRows: this.state.partialRows.filter(function(cur) {
        return cur !== id;
      })});
    },
    addedRowModified: function(id) {
      if(this.state.partialRows[this.state.partialRows.length - 1] === id) {
        this.state.partialRows.push(uuid());
        this.setState({partialRows: this.state.partialRows})
      }
    },
    addColumn: function() {
      dispatch("addColumnToTable", {table: this.state.table});
    },
    render: function() {
      var self = this;
      var fields = code.viewToFields(this.state.table);
      var rows = ixer.facts(this.state.table);
      var numColumns = fields.length;
      var headers = fields.map(function(cur) {
        return tableHeader({field: code.name(cur[0]), id: cur[0]});
      });
      var rowIds = ixer.index("editId")[this.state.table];
      if(rowIds) {
        rows.sort(function(a, b) {
          return rowIds[JSON.stringify(a)] - rowIds[JSON.stringify(b)];
        });
      }
      var rowComponents = rows.map(function(cur, ix) {
        return tableRow({table: self.props.table, row: cur, length: numColumns, key: JSON.stringify(cur) + ix, editable: true});
      });
      this.state.partialRows.forEach(function(cur) {
        rowComponents.push(tableRow({table: self.props.table, row: [], length: numColumns, editable: true, isNewRow: true, onRowAdded: self.rowAdded, onRowModified: self.addedRowModified, key: cur, id: cur}));
      });
      return JSML(["div", {className: "tableWrapper"},
                   ["table",
                    ["thead", ["tr", headers]],
                    ["tbody", rowComponents]],
                   ["div", {className: "addColumn", onClick: this.addColumn}, "+"]]);
    }
  })
};

//---------------------------------------------------------
// View components
//---------------------------------------------------------

//---------------------------------------------------------
// UI editor components
//---------------------------------------------------------

//---------------------------------------------------------
// Event dispatch
//---------------------------------------------------------

function dispatch(event, arg, noRedraw) {
  switch(event) {
    case "load":
      break;
    case "addColumnToTable":
      var diffs = code.diffs.addColumn(arg.table);
      ixer.handleDiffs(diffs);
      break;
    case "swapRow":
      var oldKey = JSON.stringify(arg.old);
      var time = ixer.index("editId")[arg.table][oldKey];
      var diffs = {
        editId: {adds: [[arg.table, JSON.stringify(arg.neue), time]], removes: [[arg.table, oldKey, time]]}
      };
      diffs[arg.table] = {adds: [arg.neue.slice()], removes: [arg.old.slice()]};
      ixer.handleDiffs(diffs);
      break;
    case "addRow":
      var diffs = {
        editId: {adds: [[arg.table, JSON.stringify(arg.neue), (new Date()).getTime()]], removes: []}
      };
      diffs[arg.table] = {adds: [arg.neue.slice()], removes: []};
      ixer.handleDiffs(diffs);
      break;
    case "rename":
      var diffs = code.diffs.changeDisplayName(arg.id, arg.value);
      ixer.handleDiffs(diffs);
      break;
    default:
      console.error("Dispatch for unknown event: ", event, arg);
      break;
  }

  if(!noRedraw) {
    React.render(root(), document.body);
  }
}

//---------------------------------------------------------
// Data API
//---------------------------------------------------------

var code = {
  diffs: {
    addColumn: function(viewId) {
      var view = ixer.index("view")[viewId];
      var fields = code.viewToFields(viewId) || [];
      var schema = view[1];
      var fieldId = uuid();
      var diffs = {
        field: {adds: [[fieldId, schema, fields.length, "unknown"]], removes: []},
        displayName: {adds: [[fieldId, alphabet[fields.length]]], removes: []}
      };
      return diffs;
    },
    changeDisplayName: function(id, neue) {
      var cur = ixer.index("displayName")[id];
      var diffs = {
        displayName: {adds: [[id, neue]], removes: [[id, cur]]}
      }
      return diffs;
    }
  },
  viewToFields: function(view) {
    var schema = ixer.index("viewToSchema")[view];
    return ixer.index("schemaToFields")[schema];
  },
  name: function(id) {
    return ixer.index("displayName")[id];
  }
}

//---------------------------------------------------------
// Go
//---------------------------------------------------------

//add some views
ixer.addIndex("displayName", "displayName", Indexing.create.lookup([0, 1]));
ixer.addIndex("view", "view", Indexing.create.lookup([0, false]));
ixer.addIndex("editId", "editId", Indexing.create.lookup([0,1,2]));
ixer.addIndex("viewToSchema", "view", Indexing.create.lookup([0, 1]));
ixer.addIndex("schemaToFields", "field", Indexing.create.collector([1]));
ixer.handleDiffs({view: {adds: [["foo", "foo-schema", false]], removes: []},
                  schema: {adds: [["foo-schema"]], removes: []},
                  field: {adds: [["foo-a", "foo-schema", 0, "string"], ["foo-b", "foo-schema", 1, "string"]], removes: []},
                  editId: {adds: [["foo", JSON.stringify(["a", "b"]), 0], ["foo", JSON.stringify(["c", "d"]), 1]], removes: []},
                  foo: {adds: [["a", "b"], ["c", "d"]], removes: []},
                  input: {adds: [["foo", ["a", "b"]], ["foo", ["c", "d"]]], removes: []},
                  displayName: {adds: [["foo-a", "foo A"], ["foo-b", "foo B"]], removes: []},
                 });

dispatch("load");
<commit_msg>Fix bad merge preventing rows from persisting<commit_after>//---------------------------------------------------------
// Utils
//---------------------------------------------------------

var alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
                "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

function reactFactory(obj) {
  return React.createFactory(React.createClass(obj));
}

function extend(dest, src) {
  for(var key in src) {
    if(!src.hasOwnProperty(key)) { continue; }
    dest[key] = src[key];
  }
  return dest;
}

function range(from, to) {
  if(to === undefined) {
    to = from;
    from = 0;
  }
  var results = [];
  for(var i = from; i < to; i++) {
    results.push(i);
  }
  return results;
}

//---------------------------------------------------------
// UI state
//---------------------------------------------------------

var uiState = {};
var ixer = new Indexing.Indexer();

//---------------------------------------------------------
// Root component
//---------------------------------------------------------

var root = reactFactory({
  displayName: "root",
  render: function() {
    return stage({parent: "body"});
  }
});

//---------------------------------------------------------
// Grid components
//---------------------------------------------------------

var tiles = {};

tiles.debug = {
  flippable: false,
  navigable: false,
  content: reactFactory({
    displayName: "debug-tile",
    render: function() {
      return JSML(["span", "hello, world!"]);
    }
  })
};

var gridTile = reactFactory({
  displayName: "grid-tile",
  render: function() {
    var tile = tiles[this.props.type];
    if(!tile) { throw new Error("Invalid tile type specified: '" + this.props.type + "'."); }

    var style = {
      top: this.props.top,
      left: this.props.left,
      width: this.props.width,
      height: this.props.height
    };

    return JSML(["div", {className: "grid-tile " + this.props.type, style: style}, tile.content(tile)]);
  }
});

var stage = reactFactory({
  displayName: "stage",
  getInitialState: function() {
    return {
      grid: Grid.makeGrid({container: this.props.parent, gutter: 8}),
      tiles: [
        {pos: [0, 0], size: [3, 1], type: "debug"},
        {pos: [3, 0], size: [9, 1], type: "debug"},
        {pos: [0, 1], size: [1, 5], type: "debug"},
        {pos: [1, 1], size: [2, 1], type: "debug"},
        {pos: [3, 1], size: [9, 1], type: "debug"},
        {pos: [1, 2], size: [4, 8], type: "table"},
        {pos: [5, 2], size: [7, 4], type: "table"}
      ]
    };
  },
  render: function() {
    var children = [];
    for(var tileIx = 0, tilesLength = this.state.tiles.length; tileIx < tilesLength; tileIx++) {
      var tileRaw = this.state.tiles[tileIx];
      var tileRect = Grid.getRect(this.state.grid, tileRaw.pos, tileRaw.size);
      var tile = extend(extend({}, tileRaw), tileRect);
      children.push(gridTile(tile));
    }
    var content = ["div", {className: "tile-grid"}];
    content.push.apply(content, children);
    return JSML(content);
  }
});

//---------------------------------------------------------
// Table components
//---------------------------------------------------------

var editable = reactFactory({
  displayName: "editable",
  getInitialState: function() {
    return {value: "", modified: false};
  },
  handleChange: function(e) {
    this.setState({value: e.target.textContent, modified: true});
  },
  handleKeys: function(e) {
    //submit on enter
    if(e.keyCode === 13) {
      this.submit();
      e.preventDefault();
    }
    if(this.props.onKey) {
      this.props.onKey(e);
    }
  },
  submit: function() {
    if(this.props.onSubmit && this.state.modified) {
      this.setState({modified: false});
      this.props.onSubmit(this.state.value);
    }
  },
  render: function() {
    var value = this.state.value || this.props.value;
    return JSML(["div", {"contentEditable": true,
                         "onInput": this.handleChange,
                         "onBlur": this.submit,
                         "onKeyDown": this.handleKeys,
                         dangerouslySetInnerHTML: {__html: value !== undefined ? value : ""}}]);
  }
});

var tableHeader = reactFactory({
  displayName: "tableHeader",
  renameHeader: function(value) {
    //do stuff
    dispatch("rename", {id: this.props.id, value: value});
  },
  render: function() {
    return JSML(["th", editable({value: this.props.field, onSubmit: this.renameHeader})]);
  }
});

var tableRow = reactFactory({
  displayName: "tableRow",
  getInitialState: function() {
    var row = [];
    var cur = this.props.row;
    for(var i = 0, len = this.props.length; i < len; i++) {
      row[i] = cur[i];
    }
    return {row: row};
  },
  componentDidUpdate: function(prev) {
    if(!Indexing.arraysIdentical(prev.row, this.props.row)) {
      var row = this.state.row;
      var cur = this.props.row;
      for(var i = 0, len = this.props.length; i < len; i++) {
        row[i] = cur[i];
      }
    }
  },
  checkRowComplete: function() {
    var row = this.state.row;
    for(var i = 0, len = this.props.length; i < len; i++) {
      if(row[i] === undefined) return false;
    }
    return true;
  },
  setColumn: function(ix, value) {
    this.state.row[ix] = value;
    if(this.checkRowComplete()) {
      this.submitRow();
    } else {
      if(this.props.onRowModified) this.props.onRowModified(this.props.id);
    }
  },
  submitRow: function() {
    if(this.props.isNewRow) {
      if(this.props.onRowAdded) this.props.onRowAdded(this.props.id);
      dispatch("addRow", {table: this.props.table, neue: this.state.row});
    } else {
      dispatch("swapRow", {table: this.props.table, old: this.props.row, neue: this.state.row});
    }
  },
  render: function() {
    var self = this;
    var fields = range(this.props.length).map(function(cur) {
      var content = self.props.row[cur];
      if(content === undefined) {
        content = "";
      }
      if(self.props.editable) {
        return ["td", editable({value: content, onSubmit: function(value) {
          self.setColumn(cur, value);
        }})];
      } else {
        return ["td", content];
      }
    });
    return JSML(["tr", fields]);
  }
});

tiles.table = {
  content: reactFactory({
    displayName: "table",
    getInitialState: function() {
      var table = "foo"; //@FIXME derive this from tableTile index.
      return {table: table, partialRows: [uuid()]};
    },
    rowAdded: function(id) {
      this.setState({partialRows: this.state.partialRows.filter(function(cur) {
        return cur !== id;
      })});
    },
    addedRowModified: function(id) {
      if(this.state.partialRows[this.state.partialRows.length - 1] === id) {
        this.state.partialRows.push(uuid());
        this.setState({partialRows: this.state.partialRows})
      }
    },
    addColumn: function() {
      dispatch("addColumnToTable", {table: this.state.table});
    },
    render: function() {
      var self = this;
      var fields = code.viewToFields(this.state.table);
      var rows = ixer.facts(this.state.table);
      var numColumns = fields.length;
      var headers = fields.map(function(cur) {
        return tableHeader({field: code.name(cur[0]), id: cur[0]});
      });
      var rowIds = ixer.index("editId")[this.state.table];
      if(rowIds) {
        rows.sort(function(a, b) {
          return rowIds[JSON.stringify(a)] - rowIds[JSON.stringify(b)];
        });
      }
      var rowComponents = rows.map(function(cur, ix) {
        return tableRow({table: self.state.table, row: cur, length: numColumns, key: JSON.stringify(cur) + ix, editable: true});
      });
      this.state.partialRows.forEach(function(cur) {
        rowComponents.push(tableRow({table: this.state.table, row: [], length: numColumns, editable: true, isNewRow: true, onRowAdded: self.rowAdded, onRowModified: self.addedRowModified, key: cur, id: cur}));
      });
      return JSML(["div", {className: "tableWrapper"},
                   ["table",
                    ["thead", ["tr", headers]],
                    ["tbody", rowComponents]],
                   ["div", {className: "addColumn", onClick: this.addColumn}, "+"]]);
    }
  })
};

//---------------------------------------------------------
// View components
//---------------------------------------------------------

//---------------------------------------------------------
// UI editor components
//---------------------------------------------------------

//---------------------------------------------------------
// Event dispatch
//---------------------------------------------------------

function dispatch(event, arg, noRedraw) {
  switch(event) {
    case "load":
      break;
    case "addColumnToTable":
      var diffs = code.diffs.addColumn(arg.table);
      ixer.handleDiffs(diffs);
      break;
    case "swapRow":
      var oldKey = JSON.stringify(arg.old);
      var time = ixer.index("editId")[arg.table][oldKey];
      var diffs = {
        editId: {adds: [[arg.table, JSON.stringify(arg.neue), time]], removes: [[arg.table, oldKey, time]]}
      };
      diffs[arg.table] = {adds: [arg.neue.slice()], removes: [arg.old.slice()]};
      ixer.handleDiffs(diffs);
      break;
    case "addRow":
      var diffs = {
        editId: {adds: [[arg.table, JSON.stringify(arg.neue), (new Date()).getTime()]], removes: []}
      };
      diffs[arg.table] = {adds: [arg.neue.slice()], removes: []};
      ixer.handleDiffs(diffs);
      break;
    case "rename":
      var diffs = code.diffs.changeDisplayName(arg.id, arg.value);
      ixer.handleDiffs(diffs);
      break;
    default:
      console.error("Dispatch for unknown event: ", event, arg);
      break;
  }

  if(!noRedraw) {
    React.render(root(), document.body);
  }
}

//---------------------------------------------------------
// Data API
//---------------------------------------------------------

var code = {
  diffs: {
    addColumn: function(viewId) {
      var view = ixer.index("view")[viewId];
      var fields = code.viewToFields(viewId) || [];
      var schema = view[1];
      var fieldId = uuid();
      var diffs = {
        field: {adds: [[fieldId, schema, fields.length, "unknown"]], removes: []},
        displayName: {adds: [[fieldId, alphabet[fields.length]]], removes: []}
      };
      return diffs;
    },
    changeDisplayName: function(id, neue) {
      var cur = ixer.index("displayName")[id];
      var diffs = {
        displayName: {adds: [[id, neue]], removes: [[id, cur]]}
      }
      return diffs;
    }
  },
  viewToFields: function(view) {
    var schema = ixer.index("viewToSchema")[view];
    return ixer.index("schemaToFields")[schema];
  },
  name: function(id) {
    return ixer.index("displayName")[id];
  }
}

//---------------------------------------------------------
// Go
//---------------------------------------------------------

//add some views
ixer.addIndex("displayName", "displayName", Indexing.create.lookup([0, 1]));
ixer.addIndex("view", "view", Indexing.create.lookup([0, false]));
ixer.addIndex("editId", "editId", Indexing.create.lookup([0,1,2]));
ixer.addIndex("viewToSchema", "view", Indexing.create.lookup([0, 1]));
ixer.addIndex("schemaToFields", "field", Indexing.create.collector([1]));
ixer.handleDiffs({view: {adds: [["foo", "foo-schema", false]], removes: []},
                  schema: {adds: [["foo-schema"]], removes: []},
                  field: {adds: [["foo-a", "foo-schema", 0, "string"], ["foo-b", "foo-schema", 1, "string"]], removes: []},
                  editId: {adds: [["foo", JSON.stringify(["a", "b"]), 0], ["foo", JSON.stringify(["c", "d"]), 1]], removes: []},
                  foo: {adds: [["a", "b"], ["c", "d"]], removes: []},
                  input: {adds: [["foo", ["a", "b"]], ["foo", ["c", "d"]]], removes: []},
                  displayName: {adds: [["foo-a", "foo A"], ["foo-b", "foo B"]], removes: []},
                 });

dispatch("load");
<|endoftext|>


<commit_before>(function() {
  var $ = jQuery.noConflict();
  var API_SUBDOMAIN = 'api';
  var API = {
    REGISTER: '/users/register'
  };
  var STORAGE = {
    RALLY_NAME_KEY: 'rally-name',
    SAVED_CONTACTS_KEY: 'saved-contacts'
  }

  // form setup (domain) & hooks
  function setupForm(selector) {
    var form = $(selector);
    if (form.length === 0) { return; }

    // Contact preference
    form.find('input[name=contact_preference]:radio').click(function () {
      contactChangeHandler(form);
    });
    contactChangeHandler(form);

    // Form handler
    form.attr('action', apiServer() + API.REGISTER);
    form.submit(submitHandler);
  }

  function contactChangeHandler(form) {
    var preference = form.find('input[name=contact_preference]:checked').val();
    form.find('input[name=phone]').attr('required', preference === 'sms');
  }

  function submitHandler(event) {
    form = $(event.target);
    event.preventDefault();

    if (!isValid(form)) {
      showFormErrors(form);
      return;
    }

    // data-offline on form to track
    if (isOffline(form)) {
      // save to localStorage
      showFormSuccess(form);
      return;
    }

    // Fall through allows form to send via AJAX
    submitFormAjax(form);
  }

  function submitFormAjax(form) {
    $.ajax({
      type: "POST",
      url: form.attr('action'),
      data: form.serialize()
    })
    .then(data => showFormSuccess(form))
    .catch(error => showAjaxErrors(form, error));
  }

  function apiServer() {
    var location = window.location;
    return location.protocol + '//' + location.host.replace('www.', API_SUBDOMAIN + '.');
  }

  // Form validation
  function isValid(form) {
    // TODO
    return true;
  }

  function showFormSuccess(form) {
    // TODO
    $(form)[0].reset();
    console.log('Successful form submission for user');
  }

  function showFormErrors(form) {
    // TODO
  }

  function showAjaxErrors(form, erorr) {
    // TODO
    console.log('Error submitting form', error);
  }

  // offline mode and stats
  function setupStatus(selector) {
    var panel = $(selector);
    var form = $('#signup');
    if (panel.length === 0) { return; }

    // Edit rally name
    $('#edit-rally-name').click(function (event) {
      var rallyName = $('#rally-name-set');
      var rallyForm = $('#rally-name-form');

      rallyName.addClass('hide');
      rallyForm.find('input').val("").on('keyup', function (event) {
        if (event.keyCode === 13) {
          setRallyName(event.target.value);

          rallyForm.addClass('hide');
          rallyName.removeClass('hide');
        }
      });
      rallyForm.removeClass('hide');
    });
    setRallyName(localStorage.getItem(STORAGE.RALLY_NAME_KEY));

    // Statistics
    updateStoredCount();
    updateTodayCount();

    // Online and offline control
    $('#go-offline').click(() => goOffline(form));
    $('#go-online').click(() => goOnline(form));
    updateOnlineStatus();

    // Auto detect offline and auto-switch
    window.addEventListener('online',  updateOnlineStatus);
    window.addEventListener('offline', updateOnlineStatus);
  }

  function updateOnlineStatus() {
    var form = $('#signup');
    if (window.navigator.onLine) {
      if (form.data('offline') === undefined) {
        // We haven't set this yet, let's do it now
        // We do not force online in case the user manually disabled it.
        goOnline(form);
      }
      $('#go-online').attr('disabled', false);
      $('#go-online-disabled').addClass('hide');
    } else {
      goOffline(form);
      $('#go-online').attr('disabled', true);
      $('#go-online-disabled').removeClass('hide');
    }
  }

  function isOffline(form) {
    return !!form.data('offline');
  }

  function goOffline(form) {
    form.data('offline', true);

    $('#status-online').addClass('hide');
    $('#status-offline').removeClass('hide');
  }

  function goOnline(form) {
    form.data('offline', false);

    $('#status-online').removeClass('hide');
    $('#status-offline').addClass('hide');
  }

  function setRallyName(name) {
    if (!name || name.trim() === "") {
      name = "Rally Name";
    }

    $('#rally-name').text(name);
    localStorage.setItem(STORAGE.RALLY_NAME_KEY, name);
  }

  function updateStoredCount() {
    var contactCount = (localStorage.getItem(STORAGE.SAVED_CONTACTS_KEY) || []).length;
    $('#rally-pending').text(contactCount);
  }

  function updateTodayCount() {
    var contacts = localStorage.getItem(STORAGE.SAVED_CONTACTS_KEY) || [];
    var todayCount = contacts.filter(function (contact) {
      // return if contact saved today
      // TODO
      return true;
    }).length;

    $('#rally-num-today').text(todayCount);
  }

  function sendPendingContacts(formTemplate) {
    // Use the passed form as a template to clone, fill data and send
    // On successful send, clear entry from localStorage
    // On error, add to errors store

    // TODO
  }

  function start() {
    setupForm('#signup');
    setupStatus('#status');
  }
  start();

})();
<commit_msg>Allow API server to work with www or bare domain<commit_after>(function() {
  var $ = jQuery.noConflict();
  var API_SUBDOMAIN = 'api';
  var API = {
    REGISTER: '/users/register'
  };
  var STORAGE = {
    RALLY_NAME_KEY: 'rally-name',
    SAVED_CONTACTS_KEY: 'saved-contacts'
  }

  // form setup (domain) & hooks
  function setupForm(selector) {
    var form = $(selector);
    if (form.length === 0) { return; }

    // Contact preference
    form.find('input[name=contact_preference]:radio').click(function () {
      contactChangeHandler(form);
    });
    contactChangeHandler(form);

    // Form handler
    form.attr('action', apiServer() + API.REGISTER);
    form.submit(submitHandler);
  }

  function contactChangeHandler(form) {
    var preference = form.find('input[name=contact_preference]:checked').val();
    form.find('input[name=phone]').attr('required', preference === 'sms');
  }

  function submitHandler(event) {
    form = $(event.target);
    event.preventDefault();

    if (!isValid(form)) {
      showFormErrors(form);
      return;
    }

    // data-offline on form to track
    if (isOffline(form)) {
      // save to localStorage
      showFormSuccess(form);
      return;
    }

    // Fall through allows form to send via AJAX
    submitFormAjax(form);
  }

  function submitFormAjax(form) {
    $.ajax({
      type: "POST",
      url: form.attr('action'),
      data: form.serialize()
    })
    .then(data => showFormSuccess(form))
    .catch(error => showAjaxErrors(form, error));
  }

  function apiServer() {
    var location = window.location;
    var baseDomain = location.host.replace('www.', '');
    return location.protocol + '//' + API_SUBDOMAIN + '.' + baseDomain;
  }

  // Form validation
  function isValid(form) {
    // TODO
    return true;
  }

  function showFormSuccess(form) {
    // TODO
    $(form)[0].reset();
    console.log('Successful form submission for user');
  }

  function showFormErrors(form) {
    // TODO
  }

  function showAjaxErrors(form, erorr) {
    // TODO
    console.log('Error submitting form', error);
  }

  // offline mode and stats
  function setupStatus(selector) {
    var panel = $(selector);
    var form = $('#signup');
    if (panel.length === 0) { return; }

    // Edit rally name
    $('#edit-rally-name').click(function (event) {
      var rallyName = $('#rally-name-set');
      var rallyForm = $('#rally-name-form');

      rallyName.addClass('hide');
      rallyForm.find('input').val("").on('keyup', function (event) {
        if (event.keyCode === 13) {
          setRallyName(event.target.value);

          rallyForm.addClass('hide');
          rallyName.removeClass('hide');
        }
      });
      rallyForm.removeClass('hide');
    });
    setRallyName(localStorage.getItem(STORAGE.RALLY_NAME_KEY));

    // Statistics
    updateStoredCount();
    updateTodayCount();

    // Online and offline control
    $('#go-offline').click(() => goOffline(form));
    $('#go-online').click(() => goOnline(form));
    updateOnlineStatus();

    // Auto detect offline and auto-switch
    window.addEventListener('online',  updateOnlineStatus);
    window.addEventListener('offline', updateOnlineStatus);
  }

  function updateOnlineStatus() {
    var form = $('#signup');
    if (window.navigator.onLine) {
      if (form.data('offline') === undefined) {
        // We haven't set this yet, let's do it now
        // We do not force online in case the user manually disabled it.
        goOnline(form);
      }
      $('#go-online').attr('disabled', false);
      $('#go-online-disabled').addClass('hide');
    } else {
      goOffline(form);
      $('#go-online').attr('disabled', true);
      $('#go-online-disabled').removeClass('hide');
    }
  }

  function isOffline(form) {
    return !!form.data('offline');
  }

  function goOffline(form) {
    form.data('offline', true);

    $('#status-online').addClass('hide');
    $('#status-offline').removeClass('hide');
  }

  function goOnline(form) {
    form.data('offline', false);

    $('#status-online').removeClass('hide');
    $('#status-offline').addClass('hide');
  }

  function setRallyName(name) {
    if (!name || name.trim() === "") {
      name = "Rally Name";
    }

    $('#rally-name').text(name);
    localStorage.setItem(STORAGE.RALLY_NAME_KEY, name);
  }

  function updateStoredCount() {
    var contactCount = (localStorage.getItem(STORAGE.SAVED_CONTACTS_KEY) || []).length;
    $('#rally-pending').text(contactCount);
  }

  function updateTodayCount() {
    var contacts = localStorage.getItem(STORAGE.SAVED_CONTACTS_KEY) || [];
    var todayCount = contacts.filter(function (contact) {
      // return if contact saved today
      // TODO
      return true;
    }).length;

    $('#rally-num-today').text(todayCount);
  }

  function sendPendingContacts(formTemplate) {
    // Use the passed form as a template to clone, fill data and send
    // On successful send, clear entry from localStorage
    // On error, add to errors store

    // TODO
  }

  function start() {
    setupForm('#signup');
    setupStatus('#status');
  }
  start();

})();
<|endoftext|>


<commit_before>// A very simple meteor app built out from the leaderboard example

Projects = new Meteor.Collection("projects");

if (Meteor.is_client) {
	// link each template value to a db query - meteor propagates any changes to the underlying data in real time, automatically
	// (note. this could perhaps be improved by reducing the number of queries and not having so many separate statements?)
	Template.statusboard.projects = function () {
		return Projects.find({}, {sort: {target: -1, status: 1}});
	};

	Template.statusboard.selected_name = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.name;
	};

	Template.statusboard.selected_status = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.status;
	};

	Template.statusboard.selected_release = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.release;
	};

	Template.statusboard.selected_target = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.target;
	};

	Template.statusboard.selected_comment = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.comment;
	};

	Template.statusboard.selected_risk = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.risk;
	};

	Template.project.selected = function () {
		return Session.equals("selected_project", this._id) ? "selected" : '';
	};

	Template.statusboard.events = {
		// update project
		'click a.update': function () {
			Projects.update(Session.get("selected_project"), {
				name: $('#project_name_edit').val(),
				release: $('#project_release_edit').val(),
				target: $('#project_target_edit').val(),
				comment: $('#project_comment_edit').val(),
				risk: $('#project_risk_edit').val(),
				status: $('#project_status_edit').val()
			});

			Session.set("selected_project", null);
		},
		// remove project
		'click a.remove': function() {
			var project = Projects.findOne(Session.get("selected_project"));
			Projects.remove({_id: project._id})
		},
		// insert a new project
		'click a.add': function () {
			var newName = $('#project_name').val();
			if (Validation.valid_name(newName)) {
				Projects.insert({
					name: newName
				})

				var project = Projects.findOne({name: newName});
				Session.set("selected_project", project._id);
			}
		},
		// nothing is selected
		'click a.cancel': function () {
			Session.set("selected_project", null);
		}
	};

	// on clicking a project, update the var which stores this
	Template.project.events = {
		'click': function () {
			Session.set("selected_project", this._id);
		}
	};

	// set the selected option for the status drop down
	var selectedStatus = Template.statusboard.selected_status;
	Handlebars.registerHelper('isStatusSelected', function(selectedStatus, optionValue) {
	  return selectedStatus == optionValue ? ' selected' : '';
	});

	// choose a colour for the status text
	Handlebars.registerHelper('getStatusColor', function(selectedStatus) {
		switch (selectedStatus) {
			case "GOOD" : {
				return '#9ACD32';
			}
			break;
			case "BAD" : {
				return 'orange';
			}
			break;
			case "UGLY" : {
				return '#EE6363';
			}
			break;
			case "IN PROGRESS" : {
				return '#7D9EC0';
			}
			break;
		}
	});

	// set the selected option for the target drop down
	var selectedTarget = Template.statusboard.selected_target;
	Handlebars.registerHelper('isTargetSelected', function(selectedTarget, optionValue) {
	  return selectedTarget == optionValue ? ' selected' : '';
	});

	Validation = {
		clear: function () {
			return Session.set("error", undefined);
		},
		set_error: function (message) {
			return Session.set("error", message);
		},
		valid_name: function (name) {
			this.clear();
			if (name.length == 0) {
				this.set_error("Name can't be blank");
				return false;
			} else if (this.project_exists(name)) {
				this.set_error("Project already exists");
				return false;
			} else {
				return true;
			}
		},
		project_exists: function(name) {
			return Projects.findOne({name: name});
		}
	};
};

// On server startup, create some projects if the database is empty. Left over from the example to show where server code would go if there were any!
if (Meteor.is_server) {
	Meteor.startup(function () {
		if (Projects.find().count() === 0) {
			var names = ["DEMO ACS",
			"DEMO CRM",
			"DEMO DRT"];
			for (var i = 0; i < names.length; i++)
			Projects.insert({name: names[i], score: Math.floor(Math.random()*10)*5, status: "OK"});
		}
	});
}

<commit_msg>Store target as integer<commit_after>// A very simple meteor app built out from the leaderboard example

Projects = new Meteor.Collection("projects");

if (Meteor.is_client) {
	// link each template value to a db query - meteor propagates any changes to the underlying data in real time, automatically
	// (note. this could perhaps be improved by reducing the number of queries and not having so many separate statements?)
	Template.statusboard.projects = function () {
		return Projects.find({}, {sort: {target: -1, status: 1}});
	};

	Template.statusboard.selected_name = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.name;
	};

	Template.statusboard.selected_status = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.status;
	};

	Template.statusboard.selected_release = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.release;
	};

	Template.statusboard.selected_target = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.target;
	};

	Template.statusboard.selected_comment = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.comment;
	};

	Template.statusboard.selected_risk = function () {
		var project = Projects.findOne(Session.get("selected_project"));
		return project && project.risk;
	};

	Template.project.selected = function () {
		return Session.equals("selected_project", this._id) ? "selected" : '';
	};

	Template.statusboard.events = {
		// update project
		'click a.update': function () {
			Projects.update(Session.get("selected_project"), {
				name: $('#project_name_edit').val(),
				release: $('#project_release_edit').val(),
				target: parseInt($('#project_target_edit').val(), 10),
				comment: $('#project_comment_edit').val(),
				risk: $('#project_risk_edit').val(),
				status: $('#project_status_edit').val()
			});

			Session.set("selected_project", null);
		},
		// remove project
		'click a.remove': function() {
			var project = Projects.findOne(Session.get("selected_project"));
			Projects.remove({_id: project._id})
		},
		// insert a new project
		'click a.add': function () {
			var newName = $('#project_name').val();
			if (Validation.valid_name(newName)) {
				Projects.insert({
					name: newName
				})

				var project = Projects.findOne({name: newName});
				Session.set("selected_project", project._id);
			}
		},
		// nothing is selected
		'click a.cancel': function () {
			Session.set("selected_project", null);
		}
	};

	// on clicking a project, update the var which stores this
	Template.project.events = {
		'click': function () {
			Session.set("selected_project", this._id);
		}
	};

	// set the selected option for the status drop down
	var selectedStatus = Template.statusboard.selected_status;
	Handlebars.registerHelper('isStatusSelected', function(selectedStatus, optionValue) {
	  return selectedStatus == optionValue ? ' selected' : '';
	});

	// choose a colour for the status text
	Handlebars.registerHelper('getStatusColor', function(selectedStatus) {
		switch (selectedStatus) {
			case "GOOD" : {
				return '#9ACD32';
			}
			break;
			case "BAD" : {
				return 'orange';
			}
			break;
			case "UGLY" : {
				return '#EE6363';
			}
			break;
			case "IN PROGRESS" : {
				return '#7D9EC0';
			}
			break;
		}
	});

	// set the selected option for the target drop down
	var selectedTarget = Template.statusboard.selected_target;
	Handlebars.registerHelper('isTargetSelected', function(selectedTarget, optionValue) {
	  return selectedTarget == optionValue ? ' selected' : '';
	});

	Validation = {
		clear: function () {
			return Session.set("error", undefined);
		},
		set_error: function (message) {
			return Session.set("error", message);
		},
		valid_name: function (name) {
			this.clear();
			if (name.length == 0) {
				this.set_error("Name can't be blank");
				return false;
			} else if (this.project_exists(name)) {
				this.set_error("Project already exists");
				return false;
			} else {
				return true;
			}
		},
		project_exists: function(name) {
			return Projects.findOne({name: name});
		}
	};
};

// On server startup, create some projects if the database is empty. Left over from the example to show where server code would go if there were any!
if (Meteor.is_server) {
	Meteor.startup(function () {
		if (Projects.find().count() === 0) {
			var names = ["DEMO ACS",
			"DEMO CRM",
			"DEMO DRT"];
			for (var i = 0; i < names.length; i++)
			Projects.insert({name: names[i], score: Math.floor(Math.random()*10)*5, status: "OK"});
		}
	});
}

<|endoftext|>


<commit_before>var main = function () {

    $('.showTooltip').tooltipster( {
        animation: 'fall',
        offsetX: 0,
        offsetY: 0,
        touchDevices: false,
        onlyOne: true,
        theme: 'tooltipster-default'
    });

    changeAnimation();

    hideBelowFold();

    //var isMobile = getMobile();
    //rearrangeForMobile(isMobile);

    //$('.contentCard a').attr("target","_blank");

};

function rearrangeForMobile(isMobile) {
    if (isMobile) {
        //$('.postList').removeClass('.completeLeft');
        //$('.postList').addClass('.center-element');
    }
}

function changeAnimation() {

    // Get current url
    var url = window.location.href;

    // Process which class
    if (url.search('about') > 0) {
        $(".navAbout > a").toggleClass("animateAboutMeColor");
        $(".navAbout > a > img").toggleClass("animateAboutMe");
    } else if (url.search('blog') > 0) {
        $(".navBlog > a").toggleClass("animateBlogColor");
        $(".navBlog > a > img").toggleClass("animateBlog");
    } else if (url.search('research') > 0 ) {
        $(".navResearch > a").toggleClass("animateResearchColor");
        $(".navResearch > a > img").toggleClass("animateResearch");
    } else if (url.search('contact') > 0 ) {
        $(".navContact > a").toggleClass("animateContactColor");
        $(".navContact > a > img").toggleClass("animateContact");
    } else {
        $(".navAbout > a").toggleClass("animateAboutMeColor");
        $(".navAbout > a > img").toggleClass("animateAboutMe");
    }

}

function hideBelowFold() {
    // Get current url
    var url = window.location.href;

    //if (url.slice(url.length-1,url.length) == '/') {
    //    url = url.slice(0,url.length-1);
    //}
    //
    //if ('/blog' == url.slice(url.length-5,url.length) ) {
    //    $('div#breakStart').nextUntil('div#breakEnd').css('display', 'none');
    //    $('div#breakStart').nextUntil('div#breakEnd').hide();
    //}

    if ((url.search('blog') > 0 || url.search('tag') > 0) && (url.search('page') > 0 ) ) {
        $('div#breakStart').nextUntil('div#breakEnd').css('display', 'none');
        $('div#breakStart').nextUntil('div#breakEnd').hide();
    }
}

function getMobile() {
    if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
        var mobile = true;
    } else {
        var mobile = false;
    }
    return mobile;
}

$(document).ready(main);<commit_msg>Fixed javascript bug<commit_after>var main = function () {

    $('.showTooltip').tooltipster( {
        animation: 'fall',
        offsetX: 0,
        offsetY: 0,
        touchDevices: false,
        onlyOne: true,
        theme: 'tooltipster-default'
    });

    changeAnimation();

    hideBelowFold();

    //var isMobile = getMobile();
    //rearrangeForMobile(isMobile);

    //$('.contentCard a').attr("target","_blank");

};

function rearrangeForMobile(isMobile) {
    if (isMobile) {
        //$('.postList').removeClass('.completeLeft');
        //$('.postList').addClass('.center-element');
    }
}

function changeAnimation() {

    // Get current url
    var url = window.location.href;

    // Process which class
    if (url.search('about') > 0) {
        $(".navAbout > a").toggleClass("animateAboutMeColor");
        $(".navAbout > a > img").toggleClass("animateAboutMe");
    } else if (url.search('blog') > 0) {
        $(".navBlog > a").toggleClass("animateBlogColor");
        $(".navBlog > a > img").toggleClass("animateBlog");
    } else if (url.search('research') > 0 ) {
        $(".navResearch > a").toggleClass("animateResearchColor");
        $(".navResearch > a > img").toggleClass("animateResearch");
    } else if (url.search('contact') > 0 ) {
        $(".navContact > a").toggleClass("animateContactColor");
        $(".navContact > a > img").toggleClass("animateContact");
    } else {
        $(".navAbout > a").toggleClass("animateAboutMeColor");
        $(".navAbout > a > img").toggleClass("animateAboutMe");
    }

}

function hideBelowFold() {
    // Get current url
    var url = window.location.href;

    //if (url.slice(url.length-1,url.length) == '/') {
    //    url = url.slice(0,url.length-1);
    //}
    //
    //if ('/blog' == url.slice(url.length-5,url.length) ) {
    //    $('div#breakStart').nextUntil('div#breakEnd').css('display', 'none');
    //    $('div#breakStart').nextUntil('div#breakEnd').hide();
    //}

    var cropURL = url.substring(0, url.lastIndexOf('/'));

    if (cropURL.search('blog') > 0 || cropURL.search('tag') > 0) {
        $('div#breakStart').nextUntil('div#breakEnd').css('display', 'none');
        $('div#breakStart').nextUntil('div#breakEnd').hide();
    }
}

function getMobile() {
    if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
        var mobile = true;
    } else {
        var mobile = false;
    }
    return mobile;
}

$(document).ready(main);<|endoftext|>


<commit_before>$(document).ready(function() {
	initMap();
    // if i want to work with geolocation, i can in stead put initlocationprocedure here
    // $(".button-collapse").sideNav();
});

// function initLocationProcedure() {
//     initMap()
//     if(navigator.geolocation) {
//         navigator.geolocation.getCurrentPosition(displayAndWatch, locError, {enableHighAccuracy: true})
//     } else {
//         alert('Sorry, your browser does not support the Geolocation')
//     }
// }

var map;
var home = {lat: 52.373522, lng: 4.858905};

function initMap() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 52.380149, lng: 4.887603},
		zoom: 12
		// https://developers.google.com/maps/documentation/javascript/reference#MapOptions
		// nog andere properties die niet verplichtz ijn
	});

	//////////////// TEST MARKER + infowindow at my own home, no changing data involved.
	// var homeContentString = '

This is where Selma lives.

'
	// var infowindow = new google.maps.InfoWindow ({
	// 	content: homeContentString
	// })
	// var marker = new google.maps.Marker({
	// 	position: home,
	// 	map: map,
	// 	title: 'Home'
	// });
	// marker.addListener('click', function () {
	// 	infowindow.open(map, marker);
	// })
	// google.maps.event.addListener(map, 'click', function () {
	// 	infowindow.close();
	// })

	$.getJSON('/json/artworks.json', function(artwork) {
		$.each(artwork, function(key, data) {
			console.log('----------------------------------')
				// console.log(data)

				//adding artwork information to infowwindow
				var artContentString =
				'Titel: ' + data.title + '
' +
				'Beschrijving: ' + data.description + '
'

				// preparing empty variables to load in the infowindow when filled
				var animalContentString = ''
				var correspondingAnimals = ''

				for (var i = 0; i < data.animals.length; i++) {

					if (data.animals[i].length !== 0) {
						//adding info about the animals in the artworks
						correspondingAnimals = correspondingAnimals + data.animals[i].name + ', '

						animalContentString = animalContentString + (
							'Naam: ' + data.animals[i].name + '
' +
							'Beschrijving: ' + data.animals[i].description + '

'
							)
					}
				}

				//Making the infowindow itself and loading the content
				var infowindow = new google.maps.InfoWindow ({
					content:
					'
Kunstwerk
' +
					artContentString +
					'Dieren in dit kunstwerk: ' + correspondingAnimals + '

' +
					'
Dieren
' +
					animalContentString
					//this proves content can consist of multiple variables, making it easier to make them.
				})

				var artLatLng = new google.maps.LatLng(data.lat, data.lng)
            //Creating a marker and putting it on the map.
            var marker = new google.maps.Marker({
            	position: artLatLng,
            	map: map,
            	title: data.title,
            })
            marker.addListener('click', function () {
            	infowindow.open(map, marker);
            })
        })
	})
}

///////////// TESTING to see whether i can upload a csv converted to json.
//Answer: yes i can, fortunately, however I will still have to figure out how to connect the animals to this as well.
//If i want to try again to see how this looks, i can comment out the part above (from getJSON onwards) and leave this part open.
// 	$.getJSON('/json/testdata_incomplete_2.json', function(artwork) {
// 		$.each(artwork, function(key, data) {

// 				//adding artwork information to infowwindow
// 				var artContentString =
// 				'Titel: ' + data.title + '
' +
// 				'Beschrijving: ' + data.description + '
'

// 				var infowindow = new google.maps.InfoWindow ({
// 					content:
// 					'
Kunstwerk
' +
// 					artContentString
// 				})
// 				console.log(data.lat)
// 				console.log(data.lng)
// 				var artLatLng = new google.maps.LatLng(data.lat, data.lng)
//             //Creating a marker and putting it on the map.
//             var marker = new google.maps.Marker({
//             	position: artLatLng,
//             	map: map,
//             	title: data.title,
//             })
//             marker.addListener('click', function () {
//             	infowindow.open(map, marker);
//             })
//         })
// 	})
// }
<commit_msg>added some comments to self<commit_after>$(document).ready(function() {
	initMap();
    // if i want to work with geolocation, i can in stead put initlocationprocedure here
    // $(".button-collapse").sideNav();
});

// function initLocationProcedure() {
//     initMap()
//     if(navigator.geolocation) {
//         navigator.geolocation.getCurrentPosition(displayAndWatch, locError, {enableHighAccuracy: true})
//     } else {
//         alert('Sorry, your browser does not support the Geolocation')
//     }
// }

var map;
var home = {lat: 52.373522, lng: 4.858905};

function initMap() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 52.380149, lng: 4.887603},
		zoom: 12
		// https://developers.google.com/maps/documentation/javascript/reference#MapOptions
		// nog andere properties die niet verplichtz ijn
	});

	//////////////// TEST MARKER + infowindow at my own home, no changing data involved.
	// var homeContentString = '

This is where Selma lives.

'
	// var infowindow = new google.maps.InfoWindow ({
	// 	content: homeContentString
	// })
	// var marker = new google.maps.Marker({
	// 	position: home,
	// 	map: map,
	// 	title: 'Home'
	// });
	// marker.addListener('click', function () {
	// 	infowindow.open(map, marker);
	// })
	// google.maps.event.addListener(map, 'click', function () {
	// 	infowindow.close();
	// })

	$.getJSON('/json/artworks.json', function(artwork) {
		$.each(artwork, function(key, data) {
			console.log('----------------------------------')
				// console.log(data)

				//adding artwork information to infowwindow
				var artContentString =
				'Titel: ' + data.title + '
' +
				'Beschrijving: ' + data.description + '
'

				// preparing empty variables to load in the infowindow when filled
				var animalContentString = ''
				var correspondingAnimals = ''

				for (var i = 0; i < data.animals.length; i++) {

					if (data.animals[i].length !== 0) {
						//adding info about the animals in the artworks
						correspondingAnimals = correspondingAnimals + data.animals[i].name + ', '

						animalContentString = animalContentString + (
							'Naam: ' + data.animals[i].name + '
' +
							'Beschrijving: ' + data.animals[i].description + '

'
							)
					}
				}

				//Making the infowindow itself and loading the content
				var infowindow = new google.maps.InfoWindow ({
					content:
					'
Kunstwerk
' +
					artContentString +
					'Dieren in dit kunstwerk: ' + correspondingAnimals + '

' +
					'
Dieren
' +
					animalContentString
					//this proves content can consist of multiple variables, making it easier to make them.
				})

				var artLatLng = new google.maps.LatLng(data.lat, data.lng)
            //Creating a marker and putting it on the map.
            var marker = new google.maps.Marker({
            	position: artLatLng,
            	map: map,
            	title: data.title,
            })
            marker.addListener('click', function () {
            	infowindow.open(map, marker);
            })
        })
	})
}

///////////// TESTING to see whether i can upload a csv converted to json.
//Answer: yes i can, fortunately, however I will still have to figure out how to connect the animals to this as well.
//If i want to try again to see how this looks, i can comment out the part above (from getJSON onwards) and leave this part open.
//TIPS TO SELF by maken van nieuwe json:
// let op lat/lng, welke is welke.
// Let op dat het met puntje is, niet met komma.
// Let op dat ze niet geconvert worden naar E+16 op het eind.
// Let op dat titels hetzelfde zijn als in de database.
// Kies hier bij input options "first row is column names" http://www.convertcsv.com/csv-to-json.htm
// 	$.getJSON('/json/testdata_incomplete_2.json', function(artwork) {
// 		$.each(artwork, function(key, data) {

// 				//adding artwork information to infowwindow
// 				var artContentString =
// 				'Titel: ' + data.title + '
' +
// 				'Beschrijving: ' + data.description + '
'

// 				var infowindow = new google.maps.InfoWindow ({
// 					content:
// 					'
Kunstwerk
' +
// 					artContentString
// 				})
// 				console.log(data.lat)
// 				console.log(data.lng)
// 				var artLatLng = new google.maps.LatLng(data.lat, data.lng)
//             //Creating a marker and putting it on the map.
//             var marker = new google.maps.Marker({
//             	position: artLatLng,
//             	map: map,
//             	title: data.title,
//             })
//             marker.addListener('click', function () {
//             	infowindow.open(map, marker);
//             })
//         })
// 	})
// }
<|endoftext|>


<commit_before>module.exports = async ({ core, context, github }) => {
  const { user, html_url: pr_url } = context.payload.pull_request;
  const { owner, repo } = context.repo;
  const { number: issue_number } = context.issue;

  // Skip validation on pull requests created by the automation bot
  if (user.login === "mlflow-automation") {
    console.log(
      "Skipping validation since this pull request was created by the automation bot"
    );
    return;
  }

  // Fetch release note category labels
  const labelsForRepoResp = await github.issues.listLabelsForRepo({
    owner,
    repo,
    per_page: 100, // the default value is 30, which is too small to fetch all labels
  });
  const releaseNoteLabels = labelsForRepoResp.data
    .map(({ name }) => name)
    .filter(name => name.startsWith("rn/"));
  console.log("Available release note category labels:");
  console.log(releaseNoteLabels);

  // Fetch release note category labels applied to this PR
  const listLabelsOnIssueResp = await github.issues.listLabelsOnIssue({
    owner,
    repo,
    issue_number,
  });
  const appliedLabels = listLabelsOnIssueResp.data
    .map(({ name }) => name)
    .filter(name => releaseNoteLabels.includes(name));

  console.log("Release note category labels applied to this PR:");
  console.log(appliedLabels);

  // If no release note category label is applied to this PR, set the action status to "failed"
  if (appliedLabels.length === 0) {
    // Make sure '.github/pull_request_template.md' contains an HTML anchor with this name
    const anchorName = "release-note-category";

    // Fragmented URL to jump to the release note category section in the PR description
    const anchorUrl = `${pr_url}#user-content-${anchorName}`;
    const message = [
      "No release note category label is applied to this PR. ",
      `Please select a checkbox in the release note category section: ${anchorUrl} `,
      "or manually apply a release note category label (e.g. 'rn/bug-fix') ",
      "if you're a maintainer of this repository. ",
      "If this job failed when a release note category label is already applied, ",
      "please re-run it.",
    ].join("");
    core.setFailed(message);
  }
};
<commit_msg>Retry release-note-category workflow (#6314)<commit_after>module.exports = async ({ core, context, github }) => {
  const { user, html_url: pr_url } = context.payload.pull_request;
  const { owner, repo } = context.repo;
  const { number: issue_number } = context.issue;

  // Skip validation on pull requests created by the automation bot
  if (user.login === "mlflow-automation") {
    console.log(
      "Skipping validation since this pull request was created by the automation bot"
    );
    return;
  }

  // Fetch release note category labels
  const labelsForRepoResp = await github.issues.listLabelsForRepo({
    owner,
    repo,
    per_page: 100, // the default value is 30, which is too small to fetch all labels
  });
  const releaseNoteLabels = labelsForRepoResp.data
    .map(({ name }) => name)
    .filter(name => name.startsWith("rn/"));
  console.log("Available release note category labels:");
  console.log(releaseNoteLabels);

  // Fetch the release-note category labels applied on this PR
  const getAppliedLabels = async () => {
    const backoffs = [0, 1, 2, 4, 8, 16];
    for (const [index, backoff] of backoffs.entries()) {
      console.log(`Attempt ${index + 1}/${backoffs.length}`);
      await new Promise(r => setTimeout(r, backoff * 1000));
      const listLabelsOnIssueResp = await github.issues.listLabelsOnIssue({
        owner,
        repo,
        issue_number,
      });
      const appliedLabels = listLabelsOnIssueResp.data
        .map(({ name }) => name)
        .filter((name) => releaseNoteLabels.includes(name));

      if (appliedLabels.length > 0) {
        return appliedLabels;
      }
    }
    return [];
  };

  const appliedLabels = await getAppliedLabels();
  console.log("Release note category labels applied to this PR:");
  console.log(appliedLabels);

  // If no release note category label is applied to this PR, set the action status to "failed"
  if (appliedLabels.length === 0) {
    // Make sure '.github/pull_request_template.md' contains an HTML anchor with this name
    const anchorName = "release-note-category";

    // Fragmented URL to jump to the release note category section in the PR description
    const anchorUrl = `${pr_url}#user-content-${anchorName}`;
    const message = [
      "No release note category label is applied to this PR. ",
      `Please select a checkbox in the release note category section: ${anchorUrl} `,
      "or manually apply a release note category label (e.g. 'rn/bug-fix') ",
      "if you're a maintainer of this repository. ",
      "If this job failed when a release note category label is already applied, ",
      "please re-run it.",
    ].join("");
    core.setFailed(message);
  }
};
<|endoftext|>


<commit_before>;(function(_, Backbone, storybase) {

  if (_.isUndefined(storybase.badges.views)) {
    storybase.badges.views = {};
  }

  var BadgeView = Backbone.View.extend({
    template: _.template($('#badge-template').html()),
    tagName: 'span',
    render: function() {
      this.$el.html(this.template(this.model.attributes));
      return this;
    }
  });

  var BadgesView = Backbone.View.extend({
    SubView: BadgeView,
    render: function() {
      this.$el.html('');

      this.collection.each(function(badge){
        var bv = new this.SubView({model: badge});
        bv.parent = this;
        this.$el.append(bv.render().el);

      }, this);

      return this;
    }

  });

  var ClickableBadgeView = BadgeView.extend({
    events: {
      'dblclick .badge': 'clicked'
    },
    clicked: function(event) {
      this.parent.clickedSubView(event, this);
    }
  });

  var StoryBadgesEditorView = BadgesView.extend({
    initialize: function(options) {
      this.userBadges = options.userBadges;
      this.storyBadges = options.storyBadges;
      this.badgesView = options.badgesView;

    },
    clickedSubView: function(event, view) {
      this.badgesView.render();
      view.remove();
    },
    SubView: ClickableBadgeView,
  });

  var RemoveBadgesView = StoryBadgesEditorView.extend({
    clickedSubView: function(event, view) {
      this.storyBadges.removeBadge(view.model);
      StoryBadgesEditorView.prototype.clickedSubView.call(this, event, view);

    }
  });

  var AddBadgesView = StoryBadgesEditorView.extend({
    clickedSubView: function(event, view) {
      this.storyBadges.addBadge(view.model);
      StoryBadgesEditorView.prototype.clickedSubView.call(this, event, view);
    }
  });

  storybase.badges.views.BadgeView = BadgeView;
  storybase.badges.views.BadgesView = BadgesView;

  storybase.badges.views.StoryBadgesEditorView = StoryBadgesEditorView;
  storybase.badges.views.AddBadgesView = AddBadgesView;
  storybase.badges.views.RemoveBadgesView = RemoveBadgesView;

})(_, Backbone, storybase);
<commit_msg>Just click to add and remove badges<commit_after>;(function(_, Backbone, storybase) {

  if (_.isUndefined(storybase.badges.views)) {
    storybase.badges.views = {};
  }

  var BadgeView = Backbone.View.extend({
    template: _.template($('#badge-template').html()),
    tagName: 'span',
    render: function() {
      this.$el.html(this.template(this.model.attributes));
      return this;
    }
  });

  var BadgesView = Backbone.View.extend({
    SubView: BadgeView,
    render: function() {
      this.$el.html('');

      this.collection.each(function(badge){
        var bv = new this.SubView({model: badge});
        bv.parent = this;
        this.$el.append(bv.render().el);

      }, this);

      return this;
    }

  });

  var ClickableBadgeView = BadgeView.extend({
    events: {
      'click .badge': 'clicked'
    },
    clicked: function(event) {
      this.parent.clickedSubView(event, this);
    }
  });

  var StoryBadgesEditorView = BadgesView.extend({
    initialize: function(options) {
      this.userBadges = options.userBadges;
      this.storyBadges = options.storyBadges;
      this.badgesView = options.badgesView;

    },
    clickedSubView: function(event, view) {
      this.badgesView.render();
      view.remove();
    },
    SubView: ClickableBadgeView,
  });

  var RemoveBadgesView = StoryBadgesEditorView.extend({
    clickedSubView: function(event, view) {
      this.storyBadges.removeBadge(view.model);
      StoryBadgesEditorView.prototype.clickedSubView.call(this, event, view);

    }
  });

  var AddBadgesView = StoryBadgesEditorView.extend({
    clickedSubView: function(event, view) {
      this.storyBadges.addBadge(view.model);
      StoryBadgesEditorView.prototype.clickedSubView.call(this, event, view);
    }
  });

  storybase.badges.views.BadgeView = BadgeView;
  storybase.badges.views.BadgesView = BadgesView;

  storybase.badges.views.StoryBadgesEditorView = StoryBadgesEditorView;
  storybase.badges.views.AddBadgesView = AddBadgesView;
  storybase.badges.views.RemoveBadgesView = RemoveBadgesView;

})(_, Backbone, storybase);
<|endoftext|>


<commit_before>
/*
 *
 * Assign jquery editable attributes on each dom object having the
 *
 *   data-trigger="editable"
 *
 * attribute.
 *
 * Thus object edition comportement is fully parametrable via HTML without
 * writing any javascript filling the following other attributes
 *
 * data-editable-tooltip : the tooltip to display on hover
 *   type: string
 *
 * data-editable-trigger : the trigger for editing element
 *   type: "click" | "dblclick" (default: click)
 *
 * data-editable-button :
 * data-editable-id :
 * data-editable-load-url :
 * data-editable-save-url :
 * data-editable-type :
 *
*/

// FIXME: make sure jquery.editable plugin is loaded

$(document).ready(function() {

	// we encapsulate editable call within each to force "this" to be correcly set...
	$("*[data-toggle='i4p-editable-button']").each(function(){
		var buttonThis = this;
		var dataTarget = '#' + $(this).attr('data-target');
		var dataTargetTrigger = $(dataTarget).attr('data-editable-trigger');

		$(this).click(function(e){
			e.preventDefault();
			$(dataTarget).trigger(dataTargetTrigger);
		});

		// hide clicked button and other for the same element once
		// FIXME: hide other buttons for the same elements
		$(dataTarget).bind(dataTargetTrigger, function() {
			$(buttonThis).fadeOut('slow');

		});
	});

	$("*[data-toggle='i4p-editable']").each(function(){
		var editableThis = this;

		$(this).editable(
			$(this).attr('data-editable-save-url'),
			{
				'event': $(editableThis).attr('data-editable-trigger'),
				tooltip: $(editableThis).attr('data-editable-tooltip'),
				type: $(editableThis).attr('data-editable-type'),
				loadurl: $(editableThis).attr('data-editable-load-url'),
				loaddata: {
					'id' : $(editableThis).attr('data-editable-id'),
					'language_code': $(editableThis).attr('data-language-code')
				},
				submitdata: function(value, settings) {
					return {
						'id' : $(editableThis).attr('data-editable-id'),
						'language_code': $(editableThis).attr('data-language-code'),
						'description': '',
						// FIXME: use the honeypot // $('.project_details_body input[name=description]').val()
					}
				},
				dataType: 'json',
				callback: function(data) {
					var res = jQuery.parseJSON(data);
					$(editableThis).html(res.text);
				},
				indicator: 'Saving...',
				cancel: $(editableThis).attr('data-editable-cancel'),
				submit: $(editableThis).attr('data-editable-submit'),
				onblur: 'ignore',
				cssclass: 'inline-edit',
				placeholder: $(editableThis).attr('data-editable-tooltip'),
				onblur: function(data) {
					// re-enable edit button
					$("*[data-toggle='i4p-editable-button']").each(function(){
						var buttonThis = this;
						var buttonTarget = $(buttonThis).attr('data-target');
						var localId = $(editableThis).attr('id');
						//console.log("scanning button with target : #" + buttonTarget);

						if (localId == buttonTarget) {
							$(buttonThis).fadeIn('slow');
						}
					});
				}
			});
	});
});
<commit_msg>js: jslint-ed i4p/editable.js<commit_after>/*
 *
 * Assign jquery editable attributes on each dom object having the
 *
 *   data-trigger="editable"
 *
 * attribute.
 *
 * Thus object edition comportement is fully parametrable via HTML without
 * writing any javascript filling the following other attributes
 *
 * data-editable-tooltip : the tooltip to display on hover
 *   type: string
 *
 * data-editable-trigger : the trigger for editing element
 *   type: "click" | "dblclick" (default: click)
 *
 * data-editable-button :
 * data-editable-id :
 * data-editable-load-url :
 * data-editable-save-url :
 * data-editable-type :
 *
*/

// require jquery
// require jquery.editable

/*jslint browser: true*/
/*global $, jQuery, document*/

"use strict";

$(document).ready(function () {

	// we encapsulate editable call within each to force "this" to be correcly set...
	$("*[data-toggle='i4p-editable-button']").each(function () {
		var buttonThis = this,
			dataTarget = '#' + $(this).attr('data-target'),
			dataTargetTrigger = $(dataTarget).attr('data-editable-trigger');

		$(this).click(function (ev) {
			ev.preventDefault();
			$(dataTarget).trigger(dataTargetTrigger);
		});

		// hide clicked button and other for the same element once
		// FIXME: hide other buttons for the same elements
		$(dataTarget).bind(dataTargetTrigger, function () {
			$(buttonThis).fadeOut('slow');
		});
	});

	$("*[data-toggle='i4p-editable']").each(function () {
		var editableThis = this;

		$(this).editable(
			$(this).attr('data-editable-save-url'),
			{
				'event': $(editableThis).attr('data-editable-trigger'),
				tooltip: $(editableThis).attr('data-editable-tooltip'),
				type: $(editableThis).attr('data-editable-type'),
				loadurl: $(editableThis).attr('data-editable-load-url'),
				loaddata: {
					'id' : $(editableThis).attr('data-editable-id'),
					'language_code': $(editableThis).attr('data-language-code')
				},
				submitdata: function (value, settings) {
					return {
						'id' : $(editableThis).attr('data-editable-id'),
						'language_code': $(editableThis).attr('data-language-code'),
						'description': '',
						// FIXME: use the honeypot // $('.project_details_body input[name=description]').val()
					};
				},
				dataType: 'json',
				callback: function (data) {
					var res = jQuery.parseJSON(data);
					$(editableThis).html(res.text);
				},
				indicator: 'Saving...',
				cancel: $(editableThis).attr('data-editable-cancel'),
				submit: $(editableThis).attr('data-editable-submit'),
				cssclass: 'inline-edit',
				placeholder: $(editableThis).attr('data-editable-tooltip'),
				//onblur: 'ignore',
				onblur: function (data) {
					// re-enable edit button
					$("*[data-toggle='i4p-editable-button']").each(function () {
						var buttonThis = this,
							buttonTarget = $(buttonThis).attr('data-target'),
							localId = $(editableThis).attr('id');
						//console.log("scanning button with target : #" + buttonTarget);

						if (localId === buttonTarget) {
							$(buttonThis).fadeIn('slow');
						}
					});
				}
			}
		);
	});
});
<|endoftext|>


<commit_before>var message_edit = (function () {
var exports = {};
var currently_editing_messages = {};

// Returns true if the edit task should end.
exports.save = function (row, from_topic_edited_only) {
    var msg_list = current_msg_list;
    var message_id;

    if (row.hasClass('recipient_row')) {
        message_id = rows.id_for_recipient_row(row);
    } else {
        message_id = rows.id(row);
    }
    var message = current_msg_list.get(message_id);
    var changed = false;

    var new_content = row.find(".message_edit_content").val();
    var topic_changed = false;
    var new_topic;
    if (message.type === "stream") {
        new_topic = row.find(".message_edit_topic").val();
        topic_changed = (new_topic !== message.subject && new_topic.trim() !== "");
    }

    // Editing a not-yet-acked message (because the original send attempt failed)
    // just results in the in-memory message being changed
    if (message.local_id !== undefined) {
        // No changes
        if (new_content === message.raw_content && !topic_changed) {
            return true;
        }
        echo.edit_locally(message, new_content, topic_changed ? new_topic : undefined);
        return true;
    }

    var request = {message_id: message.id};
    if (topic_changed) {
        request.subject = new_topic;
        if (feature_flags.propagate_topic_edits) {
            var selected_topic_propagation = row.find("select.message_edit_topic_propagate").val() || "change_later";
            request.propagate_mode = selected_topic_propagation;
        }
        changed = true;
    }

    if (new_content !== message.raw_content && !from_topic_edited_only) {
        request.content = new_content;
        message.is_me_message = new_content.lastIndexOf('/me', 0) === 0;
        changed = true;
    }
    if (!changed) {
        // If they didn't change anything, just cancel it.
        return true;
    }
    channel.post({
        url: '/json/update_message',
        data: request,
        success: function (data) {
            if (msg_list === current_msg_list) {
                return true;
            }
        },
        error: function (xhr, error_type, xhn) {
            var message = channel.xhr_error_message("Error saving edit", xhr);
            row.find(".edit_error").text(message).show();
        }
    });
    // The message will automatically get replaced when it arrives.
};

function handle_edit_keydown(from_topic_edited_only, e) {
    var row, code = e.keyCode || e.which;

    if (e.target.id === "message_edit_content" && code === 13 &&
        (e.metaKey || e.ctrlKey)) {
        row = $(".message_edit_content").filter(":focus").closest(".message_row");
        if (message_edit.save(row, from_topic_edited_only) === true) {
            message_edit.end(row);
        }
    } else if (e.target.id === "message_edit_topic" && code === 13) {
        // Hitting enter in topic field isn't so great.
        e.stopPropagation();
        e.preventDefault();
    }
}

function timer_text(seconds_left) {
    var minutes = Math.floor(seconds_left / 60);
    var seconds = seconds_left % 60;
    if (minutes >= 1) {
        return i18n.t("__minutes__ min to edit", {'minutes': minutes.toString()});
    } else if (seconds_left >= 10) {
        return i18n.t("__seconds__ sec to edit", {'seconds': (seconds - seconds % 5).toString()});
    }
    return i18n.t("__seconds__ sec to edit", {'seconds': seconds.toString()});
}

function edit_message (row, raw_content) {
    var content_top = row.find('.message_content')[0]
        .getBoundingClientRect().top;

    var message = current_msg_list.get(rows.id(row));

    // We potentially got to this function by clicking a button that implied the
    // user would be able to edit their message.  Give a little bit of buffer in
    // case the button has been around for a bit, e.g. we show the
    // edit_content_button (hovering pencil icon) as long as the user would have
    // been able to click it at the time the mouse entered the message_row. Also
    // a buffer in case their computer is slow, or stalled for a second, etc
    // If you change this number also change edit_limit_buffer in
    // zerver.views.messages.update_message_backend
    var seconds_left_buffer = 5;

    var now = new XDate();
    var seconds_left = page_params.realm_message_content_edit_limit_seconds +
        now.diffSeconds(message.timestamp * 1000);
    var can_edit_content = (page_params.realm_message_content_edit_limit_seconds === 0) ||
        (seconds_left + seconds_left_buffer > 0);

    var form = $(templates.render(
        'message_edit_form',
        {is_stream: (message.type === 'stream'),
         topic: message.subject,
         content: raw_content,
         minutes_to_edit: Math.floor(page_params.realm_message_content_edit_limit_seconds / 60)}));

    var edit_obj = {form: form, raw_content: raw_content};
    currently_editing_messages[message.id] = edit_obj;
    current_msg_list.show_edit_message(row, edit_obj);

    form.keydown(_.partial(handle_edit_keydown, false));

    if (!can_edit_content) {
        row.find('textarea.message_edit_content').attr("disabled","disabled");
    }

    // If we allow editing at all, give them at least 10 seconds to do it.
    // If you change this number also change edit_limit_buffer in
    // zerver.views.messages.update_message_backend
    var min_seconds_to_edit = 10;
    seconds_left = Math.floor(Math.max(seconds_left, min_seconds_to_edit));

    if (page_params.realm_message_content_edit_limit_seconds > 0) {
        row.find('.message-edit-timer-control-group').show();
        $('#message_edit_tooltip').tooltip({ animation: false, placement: 'left',
                                             template: '

'});
        var timer_row = row.find('.message_edit_countdown_timer');
        if (can_edit_content) {  // Add a visual timer
            // I believe these need to be defined outside the countdown_timer, since
            // row just refers to something like the currently selected message, and
            // can change out from under us
            var message_content_row = row.find('textarea.message_edit_content');
            var message_topic_row, message_topic_propagate_row;
            if (message.type === 'stream') {
                message_topic_row = row.find('input.message_edit_topic');
                message_topic_propagate_row = row.find('select.message_edit_topic_propagate');
            }
            var message_save_row = row.find('button.message_edit_save');
            // Do this right away, rather than waiting for the timer to do its first update,
            // since otherwise there is a noticeable lag
            timer_row.text(timer_text(seconds_left));
            var countdown_timer = setInterval(function () {
                if (--seconds_left <= 0) {
                    clearInterval(countdown_timer);
                    message_content_row.attr("disabled","disabled");
                    if (message.type === 'stream') {
                        message_topic_row.attr("disabled","disabled");
                        message_topic_propagate_row.hide();
                    }
                    // We don't go directly to "Topic editing only" state (with an active Save button),
                    // since it isn't clear what to do with the half-finished edit. It's nice to keep
                    // the half-finished edit around so that they can copy-paste it, but we don't want
                    // people to think "Save" will save the half-finished edit.
                    message_save_row.addClass("disabled");
                    timer_row.text(i18n.t("Time's up!"));
                } else {
                    timer_row.text(timer_text(seconds_left));
                }
            }, 1000);
        } else { // otherwise, give a hint as to why you can edit the topic but not the message content
            timer_row.text(i18n.t("Topic editing only"));
        }
    }

    var edit_row = row.find(".message_edit");
    if ((message.type === 'stream' && message.subject === compose.empty_topic_placeholder()) ||
        !can_edit_content) {
        edit_row.find(".message_edit_topic").focus();
    } else {
        edit_row.find(".message_edit_content").focus();
    }

    // Scroll to keep the message content in the same place
    var edit_top = edit_row.find('.message_edit_content')[0]
        .getBoundingClientRect().top;

    var scroll_by = edit_top - content_top + 5 /* border and padding */;
    edit_obj.scrolled_by = scroll_by;
    viewport.scrollTop(viewport.scrollTop() + scroll_by);

    if (feature_flags.propagate_topic_edits && message.local_id === undefined) {
        var topic_input = edit_row.find(".message_edit_topic");
        var original_topic = message.subject;
        topic_input.keyup( function () {
            var new_topic = topic_input.val();
            row.find('.message_edit_topic_propagate').toggle(new_topic !== original_topic);
        });
    }

    composebox_typeahead.initialize_compose_typeahead("#message_edit_content", {emoji: true});

}

function start_edit_maintaining_scroll(row, content) {
    edit_message(row, content);
    var row_bottom = row.height() + row.offset().top;
    var composebox_top = $("#compose").offset().top;
    if (row_bottom > composebox_top) {
        viewport.scrollTop(viewport.scrollTop() + row_bottom - composebox_top);
    }
}

exports.start = function (row) {
    var message = current_msg_list.get(rows.id(row));
    var msg_list = current_msg_list;
    channel.post({
        url: '/json/fetch_raw_message',
        idempotent: true,
        data: {message_id: message.id},
        success: function (data) {
            if (current_msg_list === msg_list) {
                message.raw_content = data.raw_content;
                start_edit_maintaining_scroll(row, data.raw_content);
            }
        }
    });
};

exports.start_local_failed_edit = function (row, message) {
    start_edit_maintaining_scroll(row, message.raw_content);
};

exports.start_topic_edit = function (recipient_row) {
    var form = $(templates.render('topic_edit_form'));
    current_msg_list.show_edit_topic(recipient_row, form);
    form.keydown(_.partial(handle_edit_keydown, true));
    var msg_id = rows.id_for_recipient_row(recipient_row);
    var message = current_msg_list.get(msg_id);
    var topic = message.subject;
    if (topic === compose.empty_topic_placeholder()) {
        topic = '';
    }
    form.find(".message_edit_topic").val(topic).select().focus();
};

exports.is_editing = function (id) {
    return currently_editing_messages[id] !== undefined;
};

exports.end = function (row) {
    var message = current_msg_list.get(rows.id(row));
    if (message !== undefined &&
        currently_editing_messages[message.id] !== undefined) {
        var scroll_by = currently_editing_messages[message.id].scrolled_by;
        viewport.scrollTop(viewport.scrollTop() - scroll_by);
        delete currently_editing_messages[message.id];
        current_msg_list.hide_edit_message(row);
    }
};

exports.maybe_show_edit = function (row, id) {
    if (currently_editing_messages[id] !== undefined) {
        current_msg_list.show_edit_message(row, currently_editing_messages[id]);
    }
};

$(document).on('narrow_deactivated.zulip', function (event) {
    _.each(currently_editing_messages, function (elem, idx) {
        if (current_msg_list.get(idx) !== undefined) {
            var row = current_msg_list.get_row(idx);
            current_msg_list.show_edit_message(row, elem);
        }
    });
});

return exports;
}());
<commit_msg>message_edit: Only initialize composebox_typeahead if needed.<commit_after>var message_edit = (function () {
var exports = {};
var currently_editing_messages = {};

// Returns true if the edit task should end.
exports.save = function (row, from_topic_edited_only) {
    var msg_list = current_msg_list;
    var message_id;

    if (row.hasClass('recipient_row')) {
        message_id = rows.id_for_recipient_row(row);
    } else {
        message_id = rows.id(row);
    }
    var message = current_msg_list.get(message_id);
    var changed = false;

    var new_content = row.find(".message_edit_content").val();
    var topic_changed = false;
    var new_topic;
    if (message.type === "stream") {
        new_topic = row.find(".message_edit_topic").val();
        topic_changed = (new_topic !== message.subject && new_topic.trim() !== "");
    }

    // Editing a not-yet-acked message (because the original send attempt failed)
    // just results in the in-memory message being changed
    if (message.local_id !== undefined) {
        // No changes
        if (new_content === message.raw_content && !topic_changed) {
            return true;
        }
        echo.edit_locally(message, new_content, topic_changed ? new_topic : undefined);
        return true;
    }

    var request = {message_id: message.id};
    if (topic_changed) {
        request.subject = new_topic;
        if (feature_flags.propagate_topic_edits) {
            var selected_topic_propagation = row.find("select.message_edit_topic_propagate").val() || "change_later";
            request.propagate_mode = selected_topic_propagation;
        }
        changed = true;
    }

    if (new_content !== message.raw_content && !from_topic_edited_only) {
        request.content = new_content;
        message.is_me_message = new_content.lastIndexOf('/me', 0) === 0;
        changed = true;
    }
    if (!changed) {
        // If they didn't change anything, just cancel it.
        return true;
    }
    channel.post({
        url: '/json/update_message',
        data: request,
        success: function (data) {
            if (msg_list === current_msg_list) {
                return true;
            }
        },
        error: function (xhr, error_type, xhn) {
            var message = channel.xhr_error_message("Error saving edit", xhr);
            row.find(".edit_error").text(message).show();
        }
    });
    // The message will automatically get replaced when it arrives.
};

function handle_edit_keydown(from_topic_edited_only, e) {
    var row, code = e.keyCode || e.which;

    if (e.target.id === "message_edit_content" && code === 13 &&
        (e.metaKey || e.ctrlKey)) {
        row = $(".message_edit_content").filter(":focus").closest(".message_row");
        if (message_edit.save(row, from_topic_edited_only) === true) {
            message_edit.end(row);
        }
    } else if (e.target.id === "message_edit_topic" && code === 13) {
        // Hitting enter in topic field isn't so great.
        e.stopPropagation();
        e.preventDefault();
    }
}

function timer_text(seconds_left) {
    var minutes = Math.floor(seconds_left / 60);
    var seconds = seconds_left % 60;
    if (minutes >= 1) {
        return i18n.t("__minutes__ min to edit", {'minutes': minutes.toString()});
    } else if (seconds_left >= 10) {
        return i18n.t("__seconds__ sec to edit", {'seconds': (seconds - seconds % 5).toString()});
    }
    return i18n.t("__seconds__ sec to edit", {'seconds': seconds.toString()});
}

function edit_message (row, raw_content) {
    var content_top = row.find('.message_content')[0]
        .getBoundingClientRect().top;

    var message = current_msg_list.get(rows.id(row));

    // We potentially got to this function by clicking a button that implied the
    // user would be able to edit their message.  Give a little bit of buffer in
    // case the button has been around for a bit, e.g. we show the
    // edit_content_button (hovering pencil icon) as long as the user would have
    // been able to click it at the time the mouse entered the message_row. Also
    // a buffer in case their computer is slow, or stalled for a second, etc
    // If you change this number also change edit_limit_buffer in
    // zerver.views.messages.update_message_backend
    var seconds_left_buffer = 5;

    var now = new XDate();
    var seconds_left = page_params.realm_message_content_edit_limit_seconds +
        now.diffSeconds(message.timestamp * 1000);
    var can_edit_content = (page_params.realm_message_content_edit_limit_seconds === 0) ||
        (seconds_left + seconds_left_buffer > 0);

    var form = $(templates.render(
        'message_edit_form',
        {is_stream: (message.type === 'stream'),
         topic: message.subject,
         content: raw_content,
         minutes_to_edit: Math.floor(page_params.realm_message_content_edit_limit_seconds / 60)}));

    var edit_obj = {form: form, raw_content: raw_content};
    currently_editing_messages[message.id] = edit_obj;
    current_msg_list.show_edit_message(row, edit_obj);

    form.keydown(_.partial(handle_edit_keydown, false));

    if (!can_edit_content) {
        row.find('textarea.message_edit_content').attr("disabled","disabled");
    } else {
        composebox_typeahead.initialize_compose_typeahead("#message_edit_content", {emoji: true});
    }

    // If we allow editing at all, give them at least 10 seconds to do it.
    // If you change this number also change edit_limit_buffer in
    // zerver.views.messages.update_message_backend
    var min_seconds_to_edit = 10;
    seconds_left = Math.floor(Math.max(seconds_left, min_seconds_to_edit));

    if (page_params.realm_message_content_edit_limit_seconds > 0) {
        row.find('.message-edit-timer-control-group').show();
        $('#message_edit_tooltip').tooltip({ animation: false, placement: 'left',
                                             template: '

'});
        var timer_row = row.find('.message_edit_countdown_timer');
        if (can_edit_content) {  // Add a visual timer
            // I believe these need to be defined outside the countdown_timer, since
            // row just refers to something like the currently selected message, and
            // can change out from under us
            var message_content_row = row.find('textarea.message_edit_content');
            var message_topic_row, message_topic_propagate_row;
            if (message.type === 'stream') {
                message_topic_row = row.find('input.message_edit_topic');
                message_topic_propagate_row = row.find('select.message_edit_topic_propagate');
            }
            var message_save_row = row.find('button.message_edit_save');
            // Do this right away, rather than waiting for the timer to do its first update,
            // since otherwise there is a noticeable lag
            timer_row.text(timer_text(seconds_left));
            var countdown_timer = setInterval(function () {
                if (--seconds_left <= 0) {
                    clearInterval(countdown_timer);
                    message_content_row.attr("disabled","disabled");
                    if (message.type === 'stream') {
                        message_topic_row.attr("disabled","disabled");
                        message_topic_propagate_row.hide();
                    }
                    // We don't go directly to "Topic editing only" state (with an active Save button),
                    // since it isn't clear what to do with the half-finished edit. It's nice to keep
                    // the half-finished edit around so that they can copy-paste it, but we don't want
                    // people to think "Save" will save the half-finished edit.
                    message_save_row.addClass("disabled");
                    timer_row.text(i18n.t("Time's up!"));
                } else {
                    timer_row.text(timer_text(seconds_left));
                }
            }, 1000);
        } else { // otherwise, give a hint as to why you can edit the topic but not the message content
            timer_row.text(i18n.t("Topic editing only"));
        }
    }

    var edit_row = row.find(".message_edit");
    if ((message.type === 'stream' && message.subject === compose.empty_topic_placeholder()) ||
        !can_edit_content) {
        edit_row.find(".message_edit_topic").focus();
    } else {
        edit_row.find(".message_edit_content").focus();
    }

    // Scroll to keep the message content in the same place
    var edit_top = edit_row.find('.message_edit_content')[0]
        .getBoundingClientRect().top;

    var scroll_by = edit_top - content_top + 5 /* border and padding */;
    edit_obj.scrolled_by = scroll_by;
    viewport.scrollTop(viewport.scrollTop() + scroll_by);

    if (feature_flags.propagate_topic_edits && message.local_id === undefined) {
        var topic_input = edit_row.find(".message_edit_topic");
        var original_topic = message.subject;
        topic_input.keyup( function () {
            var new_topic = topic_input.val();
            row.find('.message_edit_topic_propagate').toggle(new_topic !== original_topic);
        });
    }
}

function start_edit_maintaining_scroll(row, content) {
    edit_message(row, content);
    var row_bottom = row.height() + row.offset().top;
    var composebox_top = $("#compose").offset().top;
    if (row_bottom > composebox_top) {
        viewport.scrollTop(viewport.scrollTop() + row_bottom - composebox_top);
    }
}

exports.start = function (row) {
    var message = current_msg_list.get(rows.id(row));
    var msg_list = current_msg_list;
    channel.post({
        url: '/json/fetch_raw_message',
        idempotent: true,
        data: {message_id: message.id},
        success: function (data) {
            if (current_msg_list === msg_list) {
                message.raw_content = data.raw_content;
                start_edit_maintaining_scroll(row, data.raw_content);
            }
        }
    });
};

exports.start_local_failed_edit = function (row, message) {
    start_edit_maintaining_scroll(row, message.raw_content);
};

exports.start_topic_edit = function (recipient_row) {
    var form = $(templates.render('topic_edit_form'));
    current_msg_list.show_edit_topic(recipient_row, form);
    form.keydown(_.partial(handle_edit_keydown, true));
    var msg_id = rows.id_for_recipient_row(recipient_row);
    var message = current_msg_list.get(msg_id);
    var topic = message.subject;
    if (topic === compose.empty_topic_placeholder()) {
        topic = '';
    }
    form.find(".message_edit_topic").val(topic).select().focus();
};

exports.is_editing = function (id) {
    return currently_editing_messages[id] !== undefined;
};

exports.end = function (row) {
    var message = current_msg_list.get(rows.id(row));
    if (message !== undefined &&
        currently_editing_messages[message.id] !== undefined) {
        var scroll_by = currently_editing_messages[message.id].scrolled_by;
        viewport.scrollTop(viewport.scrollTop() - scroll_by);
        delete currently_editing_messages[message.id];
        current_msg_list.hide_edit_message(row);
    }
};

exports.maybe_show_edit = function (row, id) {
    if (currently_editing_messages[id] !== undefined) {
        current_msg_list.show_edit_message(row, currently_editing_messages[id]);
    }
};

$(document).on('narrow_deactivated.zulip', function (event) {
    _.each(currently_editing_messages, function (elem, idx) {
        if (current_msg_list.get(idx) !== undefined) {
            var row = current_msg_list.get_row(idx);
            current_msg_list.show_edit_message(row, elem);
        }
    });
});

return exports;
}());
<|endoftext|>