<commit_before>var Lobby = function(game){
  // allPlayers is the firebase service
  this.allPlayers;
  this.keyID;
  this.playerIndex;
};

// Room 2

Lobby.prototype = {

  preload: function(){

    this.userID = prompt("What is your name?");
    this.game.load.spritesheet('dude', 'assets/dude.png', 32, 48);
    this.game.load.image('room', 'assets/rooms/greenRoom.jpg');
    this.game.load.image('arrow', 'assets/cutouts/doodad.png');
  },

  create: function(){

    var self = this;

    // Pass firebase module to this instance
    this.allPlayers = this.game.state.states['Main'].players;

    // **ASYNC add player to firebase
    this.allPlayers.arr.$add({
      playerID: self.userID,
      isReady: false,
    }).then(function(urldb){

      // unique key id of player
      var id = urldb.key();

      // store id
      self.keyID = id;

      // player's index in DB
      self.playerIndex = self.allPlayers.arr.$indexFor(id);

      // on disconnect, remove player from array
      self.allPlayers.playersRef.child(id).onDisconnect().remove();
    }).then(function(urldb){
      // Updates lobby on player arrival
      var numPlayers = self.allPlayers.arr.length;
      self.allPlayers.lobbyRef.update({numPlayers: numPlayers, allPlayersReady: false});

      // Watch for changes in player array and updates lobby on player change
      self.allPlayers.arr.$watch(function(event){
        var numPlayers = self.allPlayers.arr.length;
        self.allPlayers.lobbyRef.update({numPlayers: numPlayers});
      });

      // Event listener for change to allPlayersReady on firebase, starts game
      self.allPlayers.lobbyRef.child('allPlayersReady').on('value', function(snapshot){
        if(snapshot.val()){
          self.game.state.start('Game');
        };
      });
    });

    // Room
    this.game.add.sprite(0, 0, 'room');

    // Player
    this.player = this.game.add.sprite(this.game.world.width/2, this.game.world.height/2, 'dude');
    this.player.animations.add('right', [5,6,7,8], 10, true);
    this.player.anchor.x = 0.5;
    this.player.anchor.y = 0.5;
    this.player.animations.frame = 4;

    // Ready Button
    this.rotator = this.game.add.sprite(this.game.world.width/2, this.game.world.height * 0.75, 'arrow');
    this.rotator.anchor.x = 0.5;
    this.rotator.anchor.y = 0.5;
    this.rotator.inputEnabled = true;

    // Title Text
    this.titleText = this.game.make.text(this.game.world.centerX, 100, "waiting on others...", {
      font: 'bold 50pt TheMinion',
      fill: '#FDFFB5',
      align: 'center'
    });
    this.titleText.setShadow(3, 3, 'rgba(0,0,0,0.5)', 5);
    this.titleText.anchor.set(0.5);
    this.optionCount = 1;
    this.game.add.existing(this.titleText);

  },

  update: function(){

    var self = this;

    // if rotator is held down
    if (this.rotator.input.checkPointerDown(this.game.input.activePointer)){
      this.toggleRotate();

    // if rotator is not held down
    } else {

      // grab index in case index has changed
      var index = self.allPlayers.arr.$indexFor(self.keyID);
      self.playerIndex = index;

      // stop animation
      this.player.animations.stop();
      this.player.animations.frame = 4;

      // have rotator fall back and change ready status back to false
      if (this.rotator.angle > 0){
        this.rotator.angle += -2;
        self.allPlayers.arr[self.playerIndex].isReady = false;
        self.allPlayers.arr.$save(self.playerIndex).then(function(ref){
          console.log('not ready on database!');
        });
      };
    }
  },

  toggleRotate: function(){

    var self = this;

    // if rotator has been held to ready status
    if(this.rotator.angle === 174){

      // play ready animation
      this.player.animations.play('right');

      this.playerIsReady = true;
      // grab index in case index has changed
      var index = self.allPlayers.arr.$indexFor(self.keyID);
      self.playerIndex = index;

      // change ready status on local
      self.allPlayers.arr[self.playerIndex].isReady = true;

      // save change to ready status on database
      self.allPlayers.arr.$save(self.playerIndex).then(function(ref){
        console.log('ready on database!');
      });

      // check if all players are ready
      if (self.allPlayers.arr.length > 1){
        var allPlayersReady = true;
        for (var i = 0; i < self.allPlayers.arr.length; i++){
          if (self.allPlayers.arr[i].isReady === false){
            allPlayersReady = false;
          }
        }
        // if all players ready, update database
        if (allPlayersReady){
          var numPlayers = self.allPlayers.arr.length;
          self.allPlayers.lobbyRef.update({numPlayers: numPlayers, allPlayersReady: allPlayersReady});
        }
      }

    // if rotator is not at ready status
    } else {
      this.rotator.angle += 6;
    }
  }
}<commit_msg>Edited comments<commit_after>var Lobby = function(game){
  // allPlayers is the firebase service
  this.allPlayers;
  this.keyID;
  this.playerIndex;
};

Lobby.prototype = {

  preload: function(){

    this.userID = prompt("What is your name?");
    this.game.load.spritesheet('dude', 'assets/dude.png', 32, 48);
    this.game.load.image('room', 'assets/rooms/greenRoom.jpg');
    this.game.load.image('arrow', 'assets/cutouts/doodad.png');
  },

  create: function(){

    var self = this;

    // Pass firebase module to this instance
    this.allPlayers = this.game.state.states['Main'].players;

    // Add player to firebase
    this.allPlayers.arr.$add({
      playerID: self.userID,
      isReady: false,
    }).then(function(urldb){

      // unique key id of player
      var id = urldb.key();

      // store id
      self.keyID = id;

      // player's index in DB
      self.playerIndex = self.allPlayers.arr.$indexFor(id);

      // on disconnect, remove player from array
      self.allPlayers.playersRef.child(id).onDisconnect().remove();
    }).then(function(urldb){
      // Updates lobby on player arrival
      var numPlayers = self.allPlayers.arr.length;
      self.allPlayers.lobbyRef.update({numPlayers: numPlayers, allPlayersReady: false});

      // Watch for changes in player array and updates lobby on player change
      self.allPlayers.arr.$watch(function(event){
        var numPlayers = self.allPlayers.arr.length;
        self.allPlayers.lobbyRef.update({numPlayers: numPlayers});
      });

      // Event listener for change to allPlayersReady on firebase, starts game
      self.allPlayers.lobbyRef.child('allPlayersReady').on('value', function(snapshot){
        if(snapshot.val()){
          self.game.state.start('Game');
        };
      });
    });

    // Room
    this.game.add.sprite(0, 0, 'room');

    // Player
    this.player = this.game.add.sprite(this.game.world.width/2, this.game.world.height/2, 'dude');
    this.player.animations.add('right', [5,6,7,8], 10, true);
    this.player.anchor.x = 0.5;
    this.player.anchor.y = 0.5;
    this.player.animations.frame = 4;

    // Ready Button
    this.rotator = this.game.add.sprite(this.game.world.width/2, this.game.world.height * 0.75, 'arrow');
    this.rotator.anchor.x = 0.5;
    this.rotator.anchor.y = 0.5;
    this.rotator.inputEnabled = true;

    // Title Text
    this.titleText = this.game.make.text(this.game.world.centerX, 100, "waiting on others...", {
      font: 'bold 50pt TheMinion',
      fill: '#FDFFB5',
      align: 'center'
    });
    this.titleText.setShadow(3, 3, 'rgba(0,0,0,0.5)', 5);
    this.titleText.anchor.set(0.5);
    this.optionCount = 1;
    this.game.add.existing(this.titleText);

  },

  update: function(){

    var self = this;

    // if rotator is held down
    if (this.rotator.input.checkPointerDown(this.game.input.activePointer)){
      this.toggleRotate();

    // if rotator is not held down
    } else {

      // grab index in case index has changed
      var index = self.allPlayers.arr.$indexFor(self.keyID);
      self.playerIndex = index;

      // stop animation
      this.player.animations.stop();
      this.player.animations.frame = 4;

      // have rotator fall back and change ready status back to false
      if (this.rotator.angle > 0){
        this.rotator.angle += -2;
        self.allPlayers.arr[self.playerIndex].isReady = false;
        self.allPlayers.arr.$save(self.playerIndex).then(function(ref){
          console.log('not ready on database!');
        });
      };
    }
  },

  toggleRotate: function(){

    var self = this;

    // if rotator has been held to ready status
    if(this.rotator.angle === 174){

      // play ready animation
      this.player.animations.play('right');

      this.playerIsReady = true;
      // grab index in case index has changed
      var index = self.allPlayers.arr.$indexFor(self.keyID);
      self.playerIndex = index;

      // change ready status on local
      self.allPlayers.arr[self.playerIndex].isReady = true;

      // save change to ready status on database
      self.allPlayers.arr.$save(self.playerIndex).then(function(ref){
        console.log('ready on database!');
      });

      // check if all players are ready
      if (self.allPlayers.arr.length > 1){
        var allPlayersReady = true;
        for (var i = 0; i < self.allPlayers.arr.length; i++){
          if (self.allPlayers.arr[i].isReady === false){
            allPlayersReady = false;
          }
        }
        // if all players ready, update database
        if (allPlayersReady){
          var numPlayers = self.allPlayers.arr.length;
          self.allPlayers.lobbyRef.update({numPlayers: numPlayers, allPlayersReady: allPlayersReady});
        }
      }

    // if rotator is not at ready status
    } else {
      this.rotator.angle += 6;
    }
  }
}<|endoftext|>


<commit_before>/**
    Framework for building object relational database apps
    Copyright (C) 2019  John Rogelstad

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/
import {catalog} from "./catalog.js";
import {model} from "./model.js";

function formAttr(data, feather) {
    let that;
    let stateClean;

    function handleDisableCurrency() {
        let attr = that.data.attr;
        let formFeather = that.parent().data.feather();
        let disableCurrency = that.data.disableCurrency;
        let fprop;
        let readOnly;

        if (!formFeather || !attr()) {
            disableCurrency.isReadOnly(true);
            return;
        }

        formFeather = catalog.getFeather(formFeather);
        fprop = formFeather.properties[attr()];

        readOnly = Boolean(
            !fprop || fprop.type !== "object" || fprop.format !== "money"
        );
        disableCurrency.isReadOnly(readOnly);
        if (readOnly) {
            disableCurrency(false);
        }
    }

    function handleRelationWidget() {
        let attr = that.data.attr;
        let formFeather = that.parent().data.feather();
        let relationWidget = that.data.relationWidget;
        let fprop;
        let readOnly;

        if (!formFeather || !attr()) {
            relationWidget.isReadOnly(true);
            return;
        }

        formFeather = catalog.getFeather(formFeather);
        fprop = formFeather.properties[attr()];

        readOnly = Boolean(
            !fprop || typeof fprop.type !== "object" || fprop.type.parentOf
        );
        relationWidget.isReadOnly(readOnly);
        if (readOnly) {
            relationWidget(undefined);
        }
    }

    function properties() {
        let keys;
        let formFeather = that.parent().data.feather();
        let result = [];

        if (!formFeather) {
            return result;
        }
        formFeather = catalog.getFeather(formFeather);
        keys = Object.keys(formFeather.properties || []).sort();
        return keys.map(function (key) {
            return {
                value: key,
                label: key
            };
        });
    }

    feather = feather || catalog.getFeather("FormAttr");
    that = model(data, feather);

    that.addCalculated({
        name: "properties",
        type: "array",
        function: properties
    });

    that.onChanged("attr", handleDisableCurrency);
    that.onChanged("attr", handleRelationWidget);
    stateClean = that.state().resolve("/Ready/Fetched/Clean");
    stateClean.enter(handleDisableCurrency);
    stateClean.enter(handleRelationWidget);

    return that;
}

catalog.register("models", "formAttr", formAttr);

export {formAttr};<commit_msg>Update form-attr.js<commit_after>/**
    Framework for building object relational database apps
    Copyright (C) 2019  John Rogelstad

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/
import {catalog} from "./catalog.js";
import {model} from "./model.js";

function formAttr(data, feather) {
    let that;
    let stateClean;

    function handleDataList() {
        let attr = that.data.attr;
        let formFeather = that.parent().data.feather();
        let dataList = that.data.dataList;
        let fprop;
        let readOnly;

        if (!formFeather || !attr()) {
            dataList.isReadOnly(true);
            return;
        }

        formFeather = catalog.getFeather(formFeather);
        fprop = formFeather.properties[attr()];

        readOnly = Boolean(
            !fprop || typeof fprop.type === "object" ||
            fprop.type === "boolean"
        );
        dataList.isReadOnly(readOnly);
        if (readOnly) {
            dataList("");
        }
    }

    function handleDisableCurrency() {
        let attr = that.data.attr;
        let formFeather = that.parent().data.feather();
        let disableCurrency = that.data.disableCurrency;
        let fprop;
        let readOnly;

        if (!formFeather || !attr()) {
            disableCurrency.isReadOnly(true);
            return;
        }

        formFeather = catalog.getFeather(formFeather);
        fprop = formFeather.properties[attr()];

        readOnly = Boolean(
            !fprop || fprop.type !== "object" || fprop.format !== "money"
        );
        disableCurrency.isReadOnly(readOnly);
        if (readOnly) {
            disableCurrency(false);
        }
    }

    function handleRelationWidget() {
        let attr = that.data.attr;
        let formFeather = that.parent().data.feather();
        let relationWidget = that.data.relationWidget;
        let fprop;
        let readOnly;

        if (!formFeather || !attr()) {
            relationWidget.isReadOnly(true);
            return;
        }

        formFeather = catalog.getFeather(formFeather);
        fprop = formFeather.properties[attr()];

        readOnly = Boolean(
            !fprop || typeof fprop.type !== "object" || fprop.type.parentOf
        );
        relationWidget.isReadOnly(readOnly);
        if (readOnly) {
            relationWidget(undefined);
        }
    }

    function properties() {
        let keys;
        let formFeather = that.parent().data.feather();
        let result = [];

        if (!formFeather) {
            return result;
        }
        formFeather = catalog.getFeather(formFeather);
        keys = Object.keys(formFeather.properties || []).sort();
        return keys.map(function (key) {
            return {
                value: key,
                label: key
            };
        });
    }

    feather = feather || catalog.getFeather("FormAttr");
    that = model(data, feather);

    that.addCalculated({
        name: "properties",
        type: "array",
        function: properties
    });

    that.onChanged("attr", handleDataList);
    that.onChanged("attr", handleDisableCurrency);
    that.onChanged("attr", handleRelationWidget);
    stateClean = that.state().resolve("/Ready/Fetched/Clean");
    stateClean.enter(handleDataList);
    stateClean.enter(handleDisableCurrency);
    stateClean.enter(handleRelationWidget);

    return that;
}

catalog.register("models", "formAttr", formAttr);

export {formAttr};<|endoftext|>


<commit_before>/** @jsx m */

"use strict";
require("./AuthorPage.scss");

var _ = require("underscore");
var m = require("mithril");

var OnUnload = require("../utils/OnUnload.js");
var Layout = require("../layouts/DefaultLayout.js");
var Spinner = require("../components/Spinner.js");
var ListEditor = require("../components/ListEditor.js");
var AuthorFinder = require("../components/AuthorFinder.js");
var AuthorModel = require("../models/AuthorModel.js");
var Modal = require("../components/Modal.js");

var AuthorPage = {};

AuthorPage.controller = function(options) {
  options = _.extend({id: "AuthorPage"}, options);
  OnUnload(this);
  this.user = options.user;
  this.editing = m.prop(false);
  this.saving = m.prop(false);

  if (m.route.param("authorId") === "new" && this.user.canEdit()) {
    this.author = new AuthorModel({});
    this.editing(true);
  } else {
    this.author = new AuthorModel({id: m.route.param("authorId")});
    this.author.fetch();
    this.author.get("articles").fetch();
  }
  window.author = this.author;

  var _this = this;
  this.controllers.layout = new Layout.controller(options);
  this.controllers.affiliationList = new ListEditor.controller({
    editing: _this.editing,
    items: function(val) {
      if (_.isUndefined(val)) {
        return _this.author.get("affiliations") || [];
      } else {
        _this.author.set("affiliations", val);
      }
    }
  });

  this.markDuplicate = function(otherAuthor) {
    if (confirm("Mark this author as a duplicate. This action cannot be undone.")) {
      _this.author.markDuplicate(otherAuthor);
    }
  };

  this.controllers.duplicateAuthorsFinder = new AuthorFinder.controller({
    onSelect: this.markDuplicate,
    parentAuthor: _this.author
  });
  this.controllers.duplicateAuthorsFinderModal = new Modal.controller();

  this.editClick = function() {
    if (_this.user.canEdit()) {
      _this.editing(true);
    }
  };

  this.saveClick = function() {
    _this.saving(true);
    var res = _this.author.save();
    res.then(function() {
      _this.saving(false);
      _this.editing(false);
      m.redraw();
    }, function() {
      _this.saving(false);
      if (!_this.author.hasErrors()) {
        _this.editing(false);
      }
      m.redraw();
    });
    if (_this.author.isNew()) {
      res.then(function() {
        if (!_this.author.isNew()) {
          m.route("/authors/" + _this.author.get("id"));
        }
      });
    }
  };

  this.discardClick = function() {
    if (_this.author.isNew()) {
      m.route("/authors/new");
    } else {
      _this.author.set(_this.author._serverState);
      _this.editing(false);
      _this.saving(false);
    }
  };

  this.deleteClick = function() {
    var confirmation = confirm("This will permanently delete the author");
    if (confirmation) {
      _this.saving(true);
      _this.author.destroy().then(function() {
        _this.saving(false);
        m.route("/");
      }, function(err) {
        _this.saving(false);
      });
    }
  };

  this.markAsDuplicateClick = function() {
    _this.controllers.duplicateAuthorsFinderModal.open(true);
  };
};

AuthorPage.view = function(ctrl) {
  var author = ctrl.author;
  var content;

  if (author.hasErrors()) {
    var errors = _.map(author.errors(), function(error) {
      return
{error}
;
    });

    var errorMessage =
{errors}
;
  }

  if (author.loading) {
    content = Spinner.view();
  } else if (!author.loaded) {
    content = errorMessage;
  } else {
    var articles = author.get("articles");
    var articlesContent;
    if (articles.loading) {
      articlesContent = Spinner.view();
    } else if (articles.length > 0) {
      articlesContent = (

{articles.length} Article{articles.length > 1 ? "s" : ""}

            {articles.map(function(article) { return AuthorPage.articleView(article, ctrl.user); })}

      );
    }

    if (ctrl.user.canEdit() && !author.markedDuplicate()) {
      var editButtons;
      if (ctrl.editing()) {
        editButtons = [
          <button type="button" className="btn" key="save" onclick={ctrl.saveClick} disabled={ctrl.saving()}>{ctrl.saving() ? "Saving..." : "Save"}</button>,
          <button type="button" className="btn" key="discard" onclick={ctrl.discardClick} disabled={ctrl.saving()}>Discard</button>,
        ];
      } else {
        editButtons = [
          <button type="button" className="btn" key="edit" onclick={ctrl.editClick}>Edit</button>,
          <button type="button" className="btn" key="delete" onclick={ctrl.deleteClick}>Delete</button>,
        ]
      }
    }

    if (author.markedDuplicate()) {
      var alerts =

This author is a duplicate.  Please reference the primary author.

;
    }

    var name;
    if (ctrl.editing()) {
      name = [
        {author.get("first_name")},
        {author.get("middle_name")},
        {author.get("last_name")},
      ];
    } else {
      name = author.get("fullName");
    }

    if (ctrl.controllers.duplicateAuthorsFinderModal.open()) {
      var duplicateAuthorsFinder = new Modal.view(ctrl.controllers.duplicateAuthorsFinderModal, {
        label: "Find duplicate author",
        content: new AuthorFinder.view(ctrl.controllers.duplicateAuthorsFinder)
      });
    }

    content = (

        {alerts}
        {errorMessage}

{name}

{author.get("job_title")}

Affiliations{(author.get("affiliations")||[]).length === 0 ? ": Unknown" : ""}

              {ListEditor.view(ctrl.controllers.affiliationList, {placeholder: "Add an affiliation"})}

              {editButtons}
              <button type="button" className="btn" disabled={author.markedDuplicate()} key="markDuplicate" onclick={ctrl.markAsDuplicateClick}>{author.markedDuplicate() ? "Marked" : "Mark"} as duplicate</button>

            {articlesContent}

          {duplicateAuthorsFinder}

    );
  }

  return new Layout.view(ctrl.controllers.layout, content);
};

AuthorPage.articleView = function(article, user) {
  return

    {article.get("title")}

      <button type="button" className={"btn btn_subtle bookmark " + (user.hasBookmarked("Article", article.get("id")) ? "active" : "")} onclick={user.toggleBookmark("Article", article)}>

      </button>
      ({article.get("year")}) {article.authors().etAl(3)}

;
};

module.exports = AuthorPage;
<commit_msg>AuthorPage: allow users to add a new author<commit_after>/** @jsx m */

"use strict";
require("./AuthorPage.scss");

var _ = require("underscore");
var m = require("mithril");

var OnUnload = require("../utils/OnUnload.js");
var Layout = require("../layouts/DefaultLayout.js");
var Spinner = require("../components/Spinner.js");
var ListEditor = require("../components/ListEditor.js");
var AuthorFinder = require("../components/AuthorFinder.js");
var AuthorModel = require("../models/AuthorModel.js");
var Modal = require("../components/Modal.js");

var AuthorPage = {};

AuthorPage.controller = function(options) {
  options = _.extend({id: "AuthorPage"}, options);
  OnUnload(this);
  this.user = options.user;
  this.editing = m.prop(false);
  this.saving = m.prop(false);

  if (m.route.param("authorId") === "new" && this.user.canEdit()) {
    this.author = new AuthorModel({});
    this.editing(true);
  } else {
    this.author = new AuthorModel({id: m.route.param("authorId")});
    this.author.fetch();
    this.author.get("articles").fetch();
  }
  window.author = this.author;

  var _this = this;
  this.controllers.layout = new Layout.controller(options);
  this.controllers.affiliationList = new ListEditor.controller({
    editing: _this.editing,
    items: function(val) {
      if (_.isUndefined(val)) {
        return _this.author.get("affiliations") || [];
      } else {
        _this.author.set("affiliations", val);
      }
    }
  });

  this.markDuplicate = function(otherAuthor) {
    if (confirm("Mark this author as a duplicate. This action cannot be undone.")) {
      _this.author.markDuplicate(otherAuthor);
    }
  };

  this.controllers.duplicateAuthorsFinder = new AuthorFinder.controller({
    onSelect: this.markDuplicate,
    parentAuthor: _this.author
  });
  this.controllers.duplicateAuthorsFinderModal = new Modal.controller();

  this.editClick = function() {
    if (_this.user.canEdit()) {
      _this.editing(true);
    }
  };

  this.saveClick = function() {
    _this.saving(true);
    var res = _this.author.save();
    res.then(function() {
      _this.saving(false);
      _this.editing(false);
      m.redraw();
    }, function() {
      _this.saving(false);
      if (!_this.author.hasErrors()) {
        _this.editing(false);
      }
      m.redraw();
    });
    if (_this.author.isNew()) {
      res.then(function() {
        if (!_this.author.isNew()) {
          m.route("/authors/" + _this.author.get("id"));
        }
      });
    }
  };

  this.discardClick = function() {
    if (_this.author.isNew()) {
      m.route("/authors/new");
    } else {
      _this.author.set(_this.author._serverState);
      _this.editing(false);
      _this.saving(false);
    }
  };

  this.deleteClick = function() {
    var confirmation = confirm("This will permanently delete the author");
    if (confirmation) {
      _this.saving(true);
      _this.author.destroy().then(function() {
        _this.saving(false);
        m.route("/");
      }, function(err) {
        _this.saving(false);
      });
    }
  };

  this.markAsDuplicateClick = function() {
    _this.controllers.duplicateAuthorsFinderModal.open(true);
  };
};

AuthorPage.view = function(ctrl) {
  var author = ctrl.author;
  var content;

  if (author.hasErrors()) {
    var errors = _.map(author.errors(), function(error) {
      return
{error}
;
    });

    var errorMessage =
{errors}
;
  }

  if (author.loading) {
    content = Spinner.view();
  } else if (!author.isNew() && !author.loaded) {
    content = errorMessage;
  } else {
    var articles = author.get("articles");
    var articlesContent;
    if (articles.loading) {
      articlesContent = Spinner.view();
    } else if (articles.length > 0) {
      articlesContent = (

{articles.length} Article{articles.length > 1 ? "s" : ""}

            {articles.map(function(article) { return AuthorPage.articleView(article, ctrl.user); })}

      );
    }

    if (ctrl.user.canEdit() && !author.markedDuplicate()) {
      var editButtons;
      if (ctrl.editing()) {
        editButtons = [
          <button type="button" className="btn" key="save" onclick={ctrl.saveClick} disabled={ctrl.saving()}>{ctrl.saving() ? "Saving..." : "Save"}</button>,
          <button type="button" className="btn" key="discard" onclick={ctrl.discardClick} disabled={ctrl.saving()}>Discard</button>,
        ];
      } else {
        editButtons = [
          <button type="button" className="btn" key="edit" onclick={ctrl.editClick}>Edit</button>,
          <button type="button" className="btn" key="delete" onclick={ctrl.deleteClick}>Delete</button>,
        ]
      }
    }

    if (author.markedDuplicate()) {
      var alerts =

This author is a duplicate.  Please reference the primary author.

;
    }

    var name;
    if (ctrl.editing()) {
      name = [
        {author.get("first_name")},
        {author.get("middle_name")},
        {author.get("last_name")},
      ];
    } else {
      name = author.get("fullName");
    }

    if (ctrl.controllers.duplicateAuthorsFinderModal.open()) {
      var duplicateAuthorsFinder = new Modal.view(ctrl.controllers.duplicateAuthorsFinderModal, {
        label: "Find duplicate author",
        content: new AuthorFinder.view(ctrl.controllers.duplicateAuthorsFinder)
      });
    }

    content = (

        {alerts}
        {errorMessage}

{name}

{author.get("job_title")}

Affiliations{(author.get("affiliations")||[]).length === 0 ? ": Unknown" : ""}

              {ListEditor.view(ctrl.controllers.affiliationList, {placeholder: "Add an affiliation"})}

              {editButtons}
              <button type="button" className="btn" disabled={author.markedDuplicate()} key="markDuplicate" onclick={ctrl.markAsDuplicateClick}>{author.markedDuplicate() ? "Marked" : "Mark"} as duplicate</button>

            {articlesContent}

          {duplicateAuthorsFinder}

    );
  }

  return new Layout.view(ctrl.controllers.layout, content);
};

AuthorPage.articleView = function(article, user) {
  return

    {article.get("title")}

      <button type="button" className={"btn btn_subtle bookmark " + (user.hasBookmarked("Article", article.get("id")) ? "active" : "")} onclick={user.toggleBookmark("Article", article)}>

      </button>
      ({article.get("year")}) {article.authors().etAl(3)}

;
};

module.exports = AuthorPage;
<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch';
import { API_URL } from '../api';

//** Action Creators */
const setResults = results => {
  return {
    type: 'HOME_NEW_RESULTS',
    results
  }
}

//** Async Actions */
export const getResults = ({query, option}) => {
  console.log(`${API_URL}/search/${option}/${query}`)
  return dispatch => {
    return fetch(`${API_URL}/search/${option}/${query}`)
      .then(response => response.json())
      .then(results => dispatch(setResults(results)))
      .catch(error => console.log(error))
  }
}<commit_msg>Remove console log<commit_after>import fetch from 'isomorphic-fetch';
import { API_URL } from '../api';

//** Action Creators */
const setResults = results => {
  return {
    type: 'HOME_NEW_RESULTS',
    results
  }
}

//** Async Actions */
export const getResults = ({query, option}) => {
  return dispatch => {
    return fetch(`${API_URL}/search/${option}/${query}`)
      .then(response => response.json())
      .then(results => dispatch(setResults(results)))
      .catch(error => console.log(error))
  }
}<|endoftext|>


<commit_before>import React from "react";
import styled from "styled-components";

const path = `
    M 235.00977 45.240234 L 224.64258 66.671875 L 186.9375 70.472656 L 175.11914 51.697266 L 148.99219 58.945312
    L 151.82031 81.373047 L 120.04102 98.623047 L 102.16602 82.363281 L 79.050781 101.22656 L 92.351562 121.46875
    L 69.505859 149.15625 L 47.076172 141.58789 L 32.525391 166.91797 L 52.255859 180.93555 L 41.355469 216.05469
    L 17.802734 216.85742 L 15.412109 249.60547 L 37.554688 253.75977 L 41.355469 291.46484 L 21.628906 301.99609
    L 32.638672 332.50781 L 52.255859 326.58398 L 69.505859 358.36328 L 53.646484 376.79102 L 71.736328 399.89062
    L 92.351562 386.05273 L 120.04102 408.89648 L 114.68945 433.35938 L 136.87695 445.32422 L 151.82031 426.14648
    L 186.9375 437.04883 L 190.23633 461.24805 L 214.27539 462.41992 L 224.64258 440.84961 L 262.34766 437.04883
    L 275.83594 457.51367 L 301.91211 449.46289 L 297.4668 426.14648 L 329.24609 408.89648 L 349.15234 425.12695
    L 368.18555 408.14648 L 356.93555 386.05273 L 379.78125 358.36328 L 404.98242 368.15039 L 417.68555 344.48438
    L 397.0293 326.58398 L 407.93164 291.46484 L 432.5 288.90625 L 433.67383 262.27734 L 411.73242 253.75977
    L 407.93164 216.05469 L 427.81445 203.06445 L 419.47656 178.43359 L 397.0293 180.93555 L 379.78125 149.15625
    L 395.79297 130.07812 L 380.41992 110.97656 L 356.93555 121.46875 L 329.24609 98.623047 L 335.33594 73.050781
    L 313.14844 63.304688 L 297.4668 81.373047 L 262.34766 70.472656 L 259.30078 46.783203 L 235.00977 45.240234 z
    M 82.472656 190.00781 L 119.82227 190.00781 L 158.04102 296.36133 L 196.16211 190.00781 L 233.51172 190.00781
    L 180.14258 334.09766 L 135.84375 334.09766 L 82.472656 190.00781 z M 235.3457 190.00781 L 368.14453 190.00781
    L 368.14453 218.0918 L 320.37109 218.0918 L 320.37109 334.09766 L 283.21484 334.09766 L 283.21484 218.0918
    L 235.3457 218.0918 L 235.3457 190.00781 z
`;

const StyledVTLogo = styled.div`
    color: #edf7f6;
    max-width: 30px;
    margin: 8px 8px 0 0;

    path {
        fill: currentColor;
        fill-opacity: 1;
        stroke: none;
        stroke-width: 1;
        stroke-miterlimit: 4;
        stroke-dasharray: none;
        stroke-dashoffset: 0;
    }
`;

export const VTLogo = ({ className, height = 30 }) => (
    <StyledVTLogo className={className}>
        <svg id="svg2" viewBox="0 0 512 512" height={height}>
            <defs id="defs125" />
            <path id="path4138-2" d={path} style={{}} />
        </svg>
    </StyledVTLogo>
);
<commit_msg>Fix VTLogo color styling<commit_after>import React from "react";
import styled from "styled-components";

const path = `
    M 235.00977 45.240234 L 224.64258 66.671875 L 186.9375 70.472656 L 175.11914 51.697266 L 148.99219 58.945312
    L 151.82031 81.373047 L 120.04102 98.623047 L 102.16602 82.363281 L 79.050781 101.22656 L 92.351562 121.46875
    L 69.505859 149.15625 L 47.076172 141.58789 L 32.525391 166.91797 L 52.255859 180.93555 L 41.355469 216.05469
    L 17.802734 216.85742 L 15.412109 249.60547 L 37.554688 253.75977 L 41.355469 291.46484 L 21.628906 301.99609
    L 32.638672 332.50781 L 52.255859 326.58398 L 69.505859 358.36328 L 53.646484 376.79102 L 71.736328 399.89062
    L 92.351562 386.05273 L 120.04102 408.89648 L 114.68945 433.35938 L 136.87695 445.32422 L 151.82031 426.14648
    L 186.9375 437.04883 L 190.23633 461.24805 L 214.27539 462.41992 L 224.64258 440.84961 L 262.34766 437.04883
    L 275.83594 457.51367 L 301.91211 449.46289 L 297.4668 426.14648 L 329.24609 408.89648 L 349.15234 425.12695
    L 368.18555 408.14648 L 356.93555 386.05273 L 379.78125 358.36328 L 404.98242 368.15039 L 417.68555 344.48438
    L 397.0293 326.58398 L 407.93164 291.46484 L 432.5 288.90625 L 433.67383 262.27734 L 411.73242 253.75977
    L 407.93164 216.05469 L 427.81445 203.06445 L 419.47656 178.43359 L 397.0293 180.93555 L 379.78125 149.15625
    L 395.79297 130.07812 L 380.41992 110.97656 L 356.93555 121.46875 L 329.24609 98.623047 L 335.33594 73.050781
    L 313.14844 63.304688 L 297.4668 81.373047 L 262.34766 70.472656 L 259.30078 46.783203 L 235.00977 45.240234 z
    M 82.472656 190.00781 L 119.82227 190.00781 L 158.04102 296.36133 L 196.16211 190.00781 L 233.51172 190.00781
    L 180.14258 334.09766 L 135.84375 334.09766 L 82.472656 190.00781 z M 235.3457 190.00781 L 368.14453 190.00781
    L 368.14453 218.0918 L 320.37109 218.0918 L 320.37109 334.09766 L 283.21484 334.09766 L 283.21484 218.0918
    L 235.3457 218.0918 L 235.3457 190.00781 z
`;

const StyledVTLogo = styled.div`
    align-items: center;
    color: currentColor;
    display: flex;
    justify-content: center;
    max-width: 30px;
    margin: 8px 8px 0 0;

    path {
        fill: currentColor;
        fill-opacity: 1;
        stroke: none;
        stroke-width: 1;
        stroke-miterlimit: 4;
        stroke-dasharray: none;
        stroke-dashoffset: 0;
    }
`;

export const VTLogo = ({ className, height = 30 }) => (
    <StyledVTLogo className={className}>
        <svg id="svg2" viewBox="0 0 512 512" height={height}>
            <defs id="defs125" />
            <path id="path4138-2" d={path} style={{}} />
        </svg>
    </StyledVTLogo>
);
<|endoftext|>


<commit_before>/* eslint-env jest, node */

// monkey patch node's console :-/
console.debug = console.debug || console.info;

import _ from 'lodash';
import { __deleteEverything__, configActions, configActionTypes } from '../actions';
import { Field, Form } from '../form';
import { store } from '../store';
import { DEFAULT_CLUSTER_CONFIG } from '../cluster-config';
import { toError, toAsyncError } from '../utils';
// TODO: (kans) test these things
// toIgnore, , toExtraData, toInFly, toExtraDataInFly, toExtraDataError

const expectCC = (path, expected, f) => {
  const value = _.get(store.getState().clusterConfig, f ? f(path) : path);
  expect(value).toEqual(expected);
};

const resetCC = () => store.dispatch({
  type: configActionTypes.SET, payload: DEFAULT_CLUSTER_CONFIG,
});

const updateField = (field, value) => store.dispatch(configActions.updateField(field, value));

beforeEach(() => store.dispatch(__deleteEverything__()));

test('updates a Field', () => {
  expect.assertions(4);

  const name = 'aField';
  const aField = new Field(name, {
    default: 'a',
    validator: (value, cc) => {
      expect(value).toEqual('b');
      expect(cc[name]).toEqual('b');
    },
  });

  new Form('aForm', [aField]);
  resetCC();

  expectCC(name, 'a');
  updateField(name, 'b');
  expectCC(name, 'b');
});

test('field dependency validator is called', done => {
  expect.assertions(1);

  const aName = 'aField';

  const aField = new Field(aName, {default: 'a'});
  const bField = new Field('bField', {
    default: 'c',
    dependencies: [aName],
    validator: (value, cc) => {
      expect(cc[aName]).toEqual('b');
      done();
    },
  });

  new Form('aForm', [aField, bField]);

  resetCC();
  updateField(aName, 'b');
});

test('form validator is called', done => {
  expect.assertions(3);

  const fieldName = 'aField';

  new Form('aForm', [new Field(fieldName, {default: 'a'})], {
    validator: (value, cc, oldValue) => {
      expectCC(fieldName, 'b');
      expect(value[fieldName]).toEqual('b');
      expect(oldValue[fieldName]).toEqual('a');
      done();
    },
  });

  resetCC();
  updateField(fieldName, 'b');
});

test('sync invalidation', () => {
  expect.assertions(3);

  const invalid = 'is invalid';
  const fieldName = 'aField';
  const field = new Field(fieldName, {
    default: 'a',
    validator: value => value === 'b' && invalid,
  });

  new Form('aForm', [field]);

  resetCC();
  expectCC(fieldName, undefined, toError);

  updateField(fieldName, 'b');
  expectCC(fieldName, invalid, toError);

  updateField(fieldName, 'a');
  expectCC(fieldName, undefined, toError);
});

test('async invalidation', async done => {
  expect.assertions(5);

  const invalid = 'is invalid';
  const fieldName = 'aField';

  const field = new Field(fieldName, {
    default: 'a',
    asyncValidator: (dispatch, getState, value, oldValue, isNow) => {
      expect(isNow()).toEqual(true);
      return value === 'b' && invalid;
    },
  });

  new Form('aForm', [field]);

  expectCC(fieldName, undefined, toAsyncError);

  await updateField(fieldName, 'b');
  expectCC(fieldName, invalid, toAsyncError);

  await updateField(fieldName, 'a');
  expectCC(fieldName, undefined, toAsyncError);

  done();
});
<commit_msg>frontend/tests: cleanup a bit of copy/paste<commit_after>/* eslint-env jest, node */

// monkey patch node's console :-/
console.debug = console.debug || console.info;

import _ from 'lodash';
import { __deleteEverything__, configActions, configActionTypes } from '../actions';
import { Field, Form } from '../form';
import { store } from '../store';
import { DEFAULT_CLUSTER_CONFIG } from '../cluster-config';
import { toError, toAsyncError } from '../utils';
// TODO: (kans) test these things
// toExtraData, toInFly, toExtraDataInFly, toExtraDataError

const invalid = 'is invalid';
const fieldName = 'aField';

const expectCC = (path, expected, f) => {
  const value = _.get(store.getState().clusterConfig, f ? f(path) : path);
  expect(value).toEqual(expected);
};

const resetCC = () => store.dispatch({
  type: configActionTypes.SET, payload: DEFAULT_CLUSTER_CONFIG,
});

const updateField = (field, value) => store.dispatch(configActions.updateField(field, value));

beforeEach(() => store.dispatch(__deleteEverything__()));

test('updates a Field', () => {
  expect.assertions(4);

  const name = 'aField';
  const aField = new Field(name, {
    default: 'a',
    validator: (value, cc) => {
      expect(value).toEqual('b');
      expect(cc[name]).toEqual('b');
    },
  });

  new Form('aForm', [aField]);
  resetCC();

  expectCC(name, 'a');
  updateField(name, 'b');
  expectCC(name, 'b');
});

test('field dependency validator is called', done => {
  expect.assertions(1);

  const aName = 'aField';

  const aField = new Field(aName, {default: 'a'});
  const bField = new Field('bField', {
    default: 'c',
    dependencies: [aName],
    validator: (value, cc) => {
      expect(cc[aName]).toEqual('b');
      done();
    },
  });

  new Form('aForm', [aField, bField]);

  resetCC();
  updateField(aName, 'b');
});

test('form validator is called', done => {
  expect.assertions(3);

  new Form('aForm', [new Field(fieldName, {default: 'a'})], {
    validator: (value, cc, oldValue) => {
      expectCC(fieldName, 'b');
      expect(value[fieldName]).toEqual('b');
      expect(oldValue[fieldName]).toEqual('a');
      done();
    },
  });

  resetCC();
  updateField(fieldName, 'b');
});

test('sync invalidation', () => {
  expect.assertions(3);

  const field = new Field(fieldName, {
    default: 'a',
    validator: value => value === 'b' && invalid,
  });

  new Form('aForm', [field]);

  resetCC();
  expectCC(fieldName, undefined, toError);

  updateField(fieldName, 'b');
  expectCC(fieldName, invalid, toError);

  updateField(fieldName, 'a');
  expectCC(fieldName, undefined, toError);
});

test('async invalidation', async done => {
  expect.assertions(5);

  const field = new Field(fieldName, {
    default: 'a',
    asyncValidator: (dispatch, getState, value, oldValue, isNow) => {
      expect(isNow()).toEqual(true);
      return value === 'b' && invalid;
    },
  });

  new Form('aForm', [field]);

  expectCC(fieldName, undefined, toAsyncError);

  await updateField(fieldName, 'b');
  expectCC(fieldName, invalid, toAsyncError);

  await updateField(fieldName, 'a');
  expectCC(fieldName, undefined, toAsyncError);

  done();
});
<|endoftext|>


<commit_before>var searchAddrTemplate = '{{#if meta.total_count}}\

Resultados
\

\

\
            {{#each results}}\

\
              \

\

{{formatted_address}}
\

\
              \

\
            {{/each}}\

\

\
        {{else}}\

\
          Não foram encontrados resultados\

\
        {{/if}}';

horarioTemplate = '
\

watch_later
\

\
              <fieldset class="clean-fieldset">\

\
                  <input name="id" type="hidden">\

<input class="bottom-border" name="from_hour" type="time">
\

<input class="bottom-border" name="to_hour" type="time">
\

\
                    \
                      <select name="weekday" multiple="multiple">\
                        <option value=1 data-display-as="Seg">Segunda</option>\
                        <option value=2 data-display-as="Ter">Terça</option>\
                        <option value=3 data-display-as="Qua">Quarta</option>\
                        <option value=4 data-display-as="Qui">Quinta</option>\
                        <option value=5 data-display-as="Sex">Sexta</option>\
                        <option value=6 data-display-as="Sáb">Sábado</option>\
                        <option value=7 data-display-as="Dom">Domingo</option>\
                      </select>\

\
                    \

\

\
              </fieldset>\

remove_circle_outline
\

\

';

var resourcize = function(name, resource) {
  //return '/api/' + resource + '/' + encodeURIComponent(name) + '/';
  return '/api/' + resource + '/' + name + '/';
};

Frm7.onPageInit('adicionar', function(page) {
  // input mask para telefone
  $('#add-telefone').inputmask({
    'mask': '(99) [X]9999-9999',
    'greedy': false,
    'autoUnmask': true,
    'definitions': {
      'X': {
        validator: '[9]',
        cardinality: 1,
        casing: 'upper'
      }
    }
  });

  //limpa local no mapa quando endereço muda
  $('#add-endereco').change(function() {
    addForm.invalidaMapa();
  });

  // atualiza valor visual de slider
  $$('input[type="range"]').on('input change', function(){
    $('#badge-'+this.name).html(this.value);
  });

  // form validation
  $.validator.addMethod("notNull", function(value, element) {
    return value != null;
  }, "Este campo é requerido.");

  $('#add-form').validate({
    ignore: false,
    rules: {
      nome: 'required',
      endereco: 'required',
      lat: 'required',
      long: 'required',
      telefone: {
        required: false,
        minlength: 10,
      },
      comida: {
        notNull: true,
      },
      sinopse: 'required',
    },
    errorPlacement: function(error, element) {
      var placement = $(element).data('error');
      if ('#'+placement) {
        $('#'+placement).append(error)
      } else {
        error.insertAfter(element);
      }
    },
    focusInvalid: false,
    invalidHandler: function(form, validator) { // scroll para primeiro campo invláido
      if (!validator.numberOfInvalids()) return;
      var elem = $(validator.errorList[0].element).parent().parent().parent().parent();
      console.log(elem);
      $$('.page-content').scrollTop(
        elem.position().top, // posição do primeiro erro
        500 // tempo em milis
      );
    }
  });

  // limpa form
  $('#add-form').trigger('reset');

  // carrega objeto do restaurante se for editar
  if(page.context.id != undefined) {
    addForm.id = page.context.id;
    addForm.editar = true;
    var obj = data.objects[addForm.id];
    obj.origem = obj.origem.nome;
    Frm7.formFromData("#add-form", obj);
  } else {
    addForm.id = null;
    addForm.editar = false;
  }
});

Frm7.onPageInit('busca-end', function(page) {
    templates.searchAddr = Template7.compile(searchAddrTemplate);
});

var addForm = {
  editar: false,
  id: null,
  data: {},
  results: [],
  mapaValido: false,

  openStreetSearch: function() {
    if(!$("#add-endereco").valid()) return;
    var search = $("#add-endereco").val();
    mainView.router.load({
      url: 'busca-end.html'
    });
    addForm.searchAddress(search);
  },

  // busca por endereço
  parseSearchAddress: function(json) {
    var obj = json;
    addForm.results = json.results;
    obj.meta = {
      total_count: json.results.length,
    };
    obj.objects = json;
    var html = templates.searchAddr(obj);
    $("#search-addr-results").html(html);

    Frm7.hideIndicator();
  },

  // busca por endereço
  searchAddress: function(search) {
    Frm7.showIndicator();
    var api = "https://maps.googleapis.com/maps/api/geocode/json?region=br";
    var key = "&key=" + keys.gmaps;
    if(!search) search = $("#search-addr-input").val();
    var query = "&address=" + search;
    var url = api + key + query;
    $.getJSON(url, addForm.parseSearchAddress, data.fail);
  },

  // seleciona resultado e coloca no form
  resultAddress: function(id) {
    var obj = addForm.results[id];
    console.log(obj);
    try {
      var rua = $.grep(obj.address_components, function(e){ return e.types.indexOf('route') >= 0; })[0].long_name;
      var num = $.grep(obj.address_components, function(e){ return e.types.indexOf('street_number') >= 0; })[0].long_name;
      var bairro = $.grep(obj.address_components, function(e){ return e.types.indexOf('sublocality') >= 0; })[0].long_name;
      var formatted_address = rua + ", " + num + " - " + bairro;
    } catch (e) {
      var formatted_address = obj.formatted_address;
    }
    formData = {
      'lat': obj.geometry.location.lat,
      'long': obj.geometry.location.lng,
    };
    Frm7.formFromData("#add-form", formData);
    addForm.validaMapa();
    mainView.router.back();
  },

  // usuário escolheu resultado no gmaps
  validaMapa: function() {
    $('#add-endereco-button').removeClass('button-fail');
    $('#add-endereco-button').addClass('button-ok');
    addForm.mapaValido = true;
  },

  // usuário mudou endereço, gmaps não é mais válido
  invalidaMapa: function() {
    $('#add-endereco-button').removeClass('button-ok');
    $('#add-endereco-button').addClass('button-fail');
    var formData = {
      'lat': 0,
      'long': 0,
    };
    Frm7.formFromData("#add-form", formData);
    addForm.mapaValido = false;
  },

  // POST
  formSend: function() {
    if (!$('#add-form').valid()) return false;
    if (!addForm.mapaValido) { // validação dos campos ocultos lat long
      $$('.page-content').scrollTop(
        $('#add-endereco-button').position().top, // posição do botão de endereço
        500 // tempo em milis
      );
      return false;
    }

    var api = "/api/restaurante/";
    if(addForm.editar) {
      var type = 'PUT';
      api = api + addForm.id + '/';
    }
    else { var type = 'POST'; }
    var url = SERVER + api;

    var data = Frm7.formToData('#add-form');
    data.origem = resourcize(data.origem, 'origem');
    data.regiao = resourcize(data.regiao, 'regiao');
    data.comida.forEach(function(part, index, arr) {
      arr[index] = resourcize(part, 'comida');
    });
    data.flags.forEach(function(part, index, arr) {
      arr[index] = resourcize(part, 'flag');
    });
    if(data.preco_min == "") data.preco_min = 0;
    if(data.preco_max == "") data.preco_max = 0;

    console.log(data);
    addForm.data = data;

    /*$.post(url, data,
      function(data) {
        console.log("success");
        console.log(data)
      },
      "json"
    );*/

    $.ajax({
      url: url,
      type: type,
      contentType: 'application/json',
      data: JSON.stringify(data),
      dataType: 'json',
      processData: false,
      complete: addForm.formCheck,
      xhrFields: {
          withCredentials: true
        },
        beforeSend: function(xhr, settings) {
            xhr.setRequestHeader("X-CSRFToken", user.profile.csrf_token);
        }
    })
  },

  formCheck: function(data) {
    if (data.status == 201) { // created
      alert("O restaurante " + addForm.data.nome + " foi enviado com sucesso e aguarda moderação.");
      // TODO: aqui deve chamar o patch para horário
      mainView.router.back();
    } else if (data.status == 204) { // edited
      alert("O restaurante " + addForm.data.nome + "foi editado com sucesso.");
      delete data.objects[addForm.id];
      data.downloadSingle(addForm.id);
    } else { // qualquer outro código
      alert("Ocorreu um erro ao tentar enviar o restaurante!");
    }
  },

  // novo horário na lista
  addHorario: function(data) {
    var pai = document.getElementById('list-horarios');
    var elem = document.createElement('li');
    //newElement.setAttribute('id', elementId);
    elem.innerHTML = horarioTemplate;
    pai.appendChild(elem);
    // TODO: popula com horario
  },

  // remove aquele horário da lista
  removeHorario: function(elem) {
    // TODO: if formset has id, add to deleted elems
    var li = $(elem).parent().parent().parent().parent();
    li.remove();
  },
};
<commit_msg>horários são populados ao editar restaurante<commit_after>var searchAddrTemplate = '{{#if meta.total_count}}\

Resultados
\

\

\
            {{#each results}}\

\
              \

\

{{formatted_address}}
\

\
              \

\
            {{/each}}\

\

\
        {{else}}\

\
          Não foram encontrados resultados\

\
        {{/if}}';

horarioTemplate = '
\

watch_later
\

\
              <fieldset class="clean-fieldset">\

\
                  <input name="id" type="hidden">\

<input class="bottom-border" name="from_hour" type="time">
\

<input class="bottom-border" name="to_hour" type="time">
\

\
                    \
                      <select name="weekday" multiple="multiple">\
                        <option value=1 data-display-as="Seg">Segunda</option>\
                        <option value=2 data-display-as="Ter">Terça</option>\
                        <option value=3 data-display-as="Qua">Quarta</option>\
                        <option value=4 data-display-as="Qui">Quinta</option>\
                        <option value=5 data-display-as="Sex">Sexta</option>\
                        <option value=6 data-display-as="Sáb">Sábado</option>\
                        <option value=7 data-display-as="Dom">Domingo</option>\
                      </select>\

\
                    \

\

\
              </fieldset>\

remove_circle_outline
\

\

';

var resourcize = function(name, resource) {
  //return '/api/' + resource + '/' + encodeURIComponent(name) + '/';
  return '/api/' + resource + '/' + name + '/';
};

Frm7.onPageInit('adicionar', function(page) {
  addForm.page = page;

  // input mask para telefone
  $('#add-telefone').inputmask({
    'mask': '(99) [X]9999-9999',
    'greedy': false,
    'autoUnmask': true,
    'definitions': {
      'X': {
        validator: '[9]',
        cardinality: 1,
        casing: 'upper'
      }
    }
  });

  //limpa local no mapa quando endereço muda
  $('#add-endereco').change(function() {
    addForm.invalidaMapa();
  });

  // atualiza valor visual de slider
  $$('input[type="range"]').on('input change', function(){
    $('#badge-'+this.name).html(this.value);
  });

  // form validation
  $.validator.addMethod("notNull", function(value, element) {
    return value != null;
  }, "Este campo é requerido.");

  $('#add-form').validate({
    ignore: false,
    rules: {
      nome: 'required',
      endereco: 'required',
      lat: 'required',
      long: 'required',
      telefone: {
        required: false,
        minlength: 10,
      },
      comida: {
        notNull: true,
      },
      sinopse: 'required',
    },
    errorPlacement: function(error, element) {
      var placement = $(element).data('error');
      if ('#'+placement) {
        $('#'+placement).append(error)
      } else {
        error.insertAfter(element);
      }
    },
    focusInvalid: false,
    invalidHandler: function(form, validator) { // scroll para primeiro campo invláido
      if (!validator.numberOfInvalids()) return;
      var elem = $(validator.errorList[0].element).parent().parent().parent().parent();
      console.log(elem);
      $$('.page-content').scrollTop(
        elem.position().top, // posição do primeiro erro
        500 // tempo em milis
      );
    }
  });

  // limpa form
  $('#add-form').trigger('reset');

  // carrega objeto do restaurante se for editar
  if(page.context.id != undefined) {
    addForm.id = page.context.id;
    addForm.editar = true;
    var obj = data.objects[addForm.id];
    obj.origem = obj.origem.nome;
    Frm7.formFromData("#add-form", obj);

    var horarios = obj.horarios;
    for(key in horarios) {
      addForm.addHorario(horarios[key]);
    }

  } else {
    addForm.id = null;
    addForm.editar = false;
  }
});

Frm7.onPageInit('busca-end', function(page) {
    templates.searchAddr = Template7.compile(searchAddrTemplate);
});

var addForm = {
  editar: false,
  id: null,
  data: {},
  results: [],
  mapaValido: false,

  openStreetSearch: function() {
    if(!$("#add-endereco").valid()) return;
    var search = $("#add-endereco").val();
    mainView.router.load({
      url: 'busca-end.html'
    });
    addForm.searchAddress(search);
  },

  // busca por endereço
  parseSearchAddress: function(json) {
    var obj = json;
    addForm.results = json.results;
    obj.meta = {
      total_count: json.results.length,
    };
    obj.objects = json;
    var html = templates.searchAddr(obj);
    $("#search-addr-results").html(html);

    Frm7.hideIndicator();
  },

  // busca por endereço
  searchAddress: function(search) {
    Frm7.showIndicator();
    var api = "https://maps.googleapis.com/maps/api/geocode/json?region=br";
    var key = "&key=" + keys.gmaps;
    if(!search) search = $("#search-addr-input").val();
    var query = "&address=" + search;
    var url = api + key + query;
    $.getJSON(url, addForm.parseSearchAddress, data.fail);
  },

  // seleciona resultado e coloca no form
  resultAddress: function(id) {
    var obj = addForm.results[id];
    console.log(obj);
    try {
      var rua = $.grep(obj.address_components, function(e){ return e.types.indexOf('route') >= 0; })[0].long_name;
      var num = $.grep(obj.address_components, function(e){ return e.types.indexOf('street_number') >= 0; })[0].long_name;
      var bairro = $.grep(obj.address_components, function(e){ return e.types.indexOf('sublocality') >= 0; })[0].long_name;
      var formatted_address = rua + ", " + num + " - " + bairro;
    } catch (e) {
      var formatted_address = obj.formatted_address;
    }
    formData = {
      'lat': obj.geometry.location.lat,
      'long': obj.geometry.location.lng,
    };
    Frm7.formFromData("#add-form", formData);
    addForm.validaMapa();
    mainView.router.back();
  },

  // usuário escolheu resultado no gmaps
  validaMapa: function() {
    $('#add-endereco-button').removeClass('button-fail');
    $('#add-endereco-button').addClass('button-ok');
    addForm.mapaValido = true;
  },

  // usuário mudou endereço, gmaps não é mais válido
  invalidaMapa: function() {
    $('#add-endereco-button').removeClass('button-ok');
    $('#add-endereco-button').addClass('button-fail');
    var formData = {
      'lat': 0,
      'long': 0,
    };
    Frm7.formFromData("#add-form", formData);
    addForm.mapaValido = false;
  },

  // POST
  formSend: function() {
    if (!$('#add-form').valid()) return false;
    if (!addForm.mapaValido) { // validação dos campos ocultos lat long
      $$('.page-content').scrollTop(
        $('#add-endereco-button').position().top, // posição do botão de endereço
        500 // tempo em milis
      );
      return false;
    }

    var api = "/api/restaurante/";
    if(addForm.editar) {
      var type = 'PUT';
      api = api + addForm.id + '/';
    }
    else { var type = 'POST'; }
    var url = SERVER + api;

    var data = Frm7.formToData('#add-form');
    data.origem = resourcize(data.origem, 'origem');
    data.regiao = resourcize(data.regiao, 'regiao');
    data.comida.forEach(function(part, index, arr) {
      arr[index] = resourcize(part, 'comida');
    });
    data.flags.forEach(function(part, index, arr) {
      arr[index] = resourcize(part, 'flag');
    });
    if(data.preco_min == "") data.preco_min = 0;
    if(data.preco_max == "") data.preco_max = 0;

    console.log(data);
    addForm.data = data;

    /*$.post(url, data,
      function(data) {
        console.log("success");
        console.log(data)
      },
      "json"
    );*/

    $.ajax({
      url: url,
      type: type,
      contentType: 'application/json',
      data: JSON.stringify(data),
      dataType: 'json',
      processData: false,
      complete: addForm.formCheck,
      xhrFields: {
          withCredentials: true
        },
        beforeSend: function(xhr, settings) {
            xhr.setRequestHeader("X-CSRFToken", user.profile.csrf_token);
        }
    })
  },

  formCheck: function(data) {
    if (data.status == 201) { // created
      alert("O restaurante " + addForm.data.nome + " foi enviado com sucesso e aguarda moderação.");
      // TODO: aqui deve chamar o patch para horário
      mainView.router.back();
    } else if (data.status == 204) { // edited
      alert("O restaurante " + addForm.data.nome + "foi editado com sucesso.");
      delete data.objects[addForm.id];
      data.downloadSingle(addForm.id);
    } else { // qualquer outro código
      alert("Ocorreu um erro ao tentar enviar o restaurante!");
    }
  },

  // novo horário na lista
  addHorario: function(data) {
    var pai = document.getElementById('list-horarios');
    var elem = document.createElement('li');
    elem.innerHTML = horarioTemplate;
    pai.appendChild(elem);
    // popula com horario
    $(elem).find('input[name$="id"]').val(data.id);
    $(elem).find('input[name$="from_hour"]').val(data.from_hour);
    $(elem).find('input[name$="to_hour"]').val(data.to_hour);
    $(elem).find('select[name$="weekday"]').val(data.weekday);
    Frm7.initSmartSelects(elem);
  },

  // remove aquele horário da lista
  removeHorario: function(elem) {
    // isto pega o
 referente ao botão pressionado
    var li = $(elem).parent().parent().parent().parent();
    li.remove();
    // TODO: se formset deste elemento tem id, adiciona para lista de ids removidos
  },
};
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

// TODO:
//   - should all storage items with the privateData flag be sanitizeable? i.e.
//     local-marks, url-marks, quick-marks, macros. Bookmarks et al aren't
//     sanitizeable in FF.
//   - add warning for TIMESPAN_EVERYTHING?
//   - respect privacy.clearOnShutdown et al or recommend VimperatorLeave autocommand?
//   - add support for :set sanitizeitems=all like 'eventignore'?
//   - integrate with the Clear Private Data dialog?

// FIXME:
//   - finish FF 3.0 support if we're going to support that in Vimperator 2.2

function Sanitizer() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    const local = {}; // XXX: is there some reason liberator.loadModule doesn't create modules with new?
    services.get("subscriptLoader").loadSubScript("chrome://browser/content/sanitize.js", local);
    const Sanitizer = local.Sanitizer;

    var prefToArgMap = {
        cache: "cache",
        cookies: "cookies",
        offlineApps: "offlineapps",
        history: "history",
        formdata: "formdata",
        downloads: "downloads",
        passwords: "passwords",
        sessions: "sessions",
        siteSettings: "sitesettings",
        commandLine: "commandline"
    };

    function prefToArg(pref) prefToArgMap[pref.replace(/.*\./, "")]
    function argToPref(arg) [p for ([p, a] in Iterator(prefToArgMap)) if (a == arg)][0]

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// OPTIONS /////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    options.add(["sanitizeitems", "si"],
        "The default list of private items to sanitize",
        "stringlist", "history,formdata,cookies,cache,sessions,commandline",
        {
            setter: function (values)
            {
                for (let [, pref] in Iterator(sanitizer.prefNames))
                {
                    options.setPref(pref, false);

                    for (let [, value] in Iterator(values.split(",")))
                    {
                        if (prefToArg(pref) == value)
                        {
                            options.setPref(pref, true);
                            break;
                        }
                    }
                }

                return values;
            },
            getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(prefToArg).join(","),
            completer: function (value) [
                ["cache", "Cache"],
                ["cookies", "Cookies"],
                ["offlineapps", "Offline website data"],
                ["history", "Browsing history"],
                ["formdata", "Saved form and search history"],
                ["downloads", "Download history"],
                ["passwords", "Saved passwords"],
                ["sessions", "Authenticated sessions"],
                ["sitesettings", "Site preferences"],
                ["commandline", "Command-line history"]
            ],
            validator: Option.validateCompleter
        });

    options.add(["sanitizetimespan", "sts"],
        "The default sanitizer time span",
        "number", 1,
        {
            setter: function (value)
            {
                options.setPref("privacy.sanitize.timeSpan", value);
                return value;
            },
            getter: function () options.getPref("privacy.sanitize.timeSpan", this.defaultValue),
            completer: function (value) [
                ["0", "Everything"],
                ["1", "Last hour"],
                ["2", "Last two hours"],
                ["3", "Last four hours"],
                ["4", "Today"]
            ],
            validator: Option.validateCompleter
        });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    commands.add(["sa[nitize]"],
        "Clear private data",
        function (args)
        {
            if (options['private'])
                return void liberator.echomsg("Cannot sanitize items in private mode");

            let timespan = args["-timespan"] || options["sanitizetimespan"];

            sanitizer.range = Sanitizer.getClearRange(timespan);
            sanitizer.ignoreTimespan = !sanitizer.range;

            if (args.bang)
            {
                if (args.length > 0)
                    return void liberator.echoerr("E488: Trailing characters");

                liberator.log("Sanitizing all items in 'sanitizeitems'...");

                let errors = sanitizer.sanitize();

                if (errors)
                {
                    for (let item in errors)
                        liberator.echoerr("Error sanitizing " + item + ": " + errors[item]);
                }
            }
            else
            {
                if (args.length == 0)
                    return void liberator.echoerr("E471: Argument required");

                for (let [, item] in Iterator(args.map(argToPref)))
                {
                    liberator.log("Sanitizing " + item + " items...");

                    if (sanitizer.canClearItem(item))
                    {
                        try
                        {
                            sanitizer.clearItem(item);
                        }
                        catch (e)
                        {
                            liberator.echoerr("Error sanitizing " + item + ": " + e);
                        }
                    }
                    else
                        liberator.echomsg("Cannot sanitize " + item);
                }
            }
        },
        {
            argCount: "*", // FIXME: should be + and 0
            bang: true,
            completer: function (context) {
                context.title = ["Privacy Item", "Description"];
                context.completions = options.get("sanitizeitems").completer();
            },
            options: [
                [["-timespan"],
                 commands.OPTION_INT,
                 function (arg) /^[0-4]$/.test(arg),
                 function () options.get("sanitizetimespan").completer()]
             ]
        });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    var self = new Sanitizer();

    // TODO: remove this version test
    if (/^1.9.1/.test(services.get("xulAppInfo").platformVersion))
        self.prefDomain = "privacy.cpd.";
    else
        self.prefDomain = "privacy.item.";

    self.prefDomain2 = "extensions.liberator.privacy.cpd.";
    self.items.commandLine = {
        canClear: true,
        clear: function ()
        {
            let stores = ["command", "search"];

            if (self.range)
            {
                stores.forEach(function (store) {
                    storage["history-" + store].mutate("filter", function (item) {
                        let timestamp = item.timestamp * 1000;
                        return timestamp < self.range[0] || timestamp > self.range[1];
                    });
                });
            }
            else
                stores.forEach(function (store) { storage["history-" + store].truncate(0); });
        }
    };

    // FIXME
    // create liberator-specific sanitize prefs
    if (options.getPref(self.prefDomain2 + "commandLine") == null)
        options.setPref(self.prefDomain2 + "commandLine", false)

    self.getClearRange = Sanitizer.getClearRange;

    // Largely ripped from from browser/base/content/sanitize.js so we can override
    // the pref strategy without stepping on the global prefs namespace.
    self.sanitize = function () {
        const prefService = services.get("pref");
        let branch = prefService.getBranch(this.prefDomain);
        let branch2 = prefService.getBranch(this.prefDomain2);
        let errors = null;

        function prefSet(name)
        {
            try
            {
                return branch.getBoolPref(name);
            }
            catch (e)
            {
                return branch2.getBoolPref(name);
            }
        }

        // Cache the range of times to clear
        if (this.ignoreTimespan)
            var range = null;  // If we ignore timespan, clear everything
        else
            range = this.range || Sanitizer.getClearRange();

        for (let itemName in this.items)
        {
            let item = this.items[itemName];
            item.range = range;

            if ("clear" in item && item.canClear && prefSet(itemName))
            {
                liberator.log("Sanitizing " + itemName + " items...");
                // Some of these clear() may raise exceptions (see bug #265028)
                // to sanitize as much as possible, we catch and store them,
                // rather than fail fast.
                // Callers should check returned errors and give user feedback
                // about items that could not be sanitized
                try
                {
                    item.clear();
                }
                catch (e)
                {
                    if (!errors)
                        errors = {};
                    errors[itemName] = e;
                    dump("Error sanitizing " + itemName + ": " + e + "\n");
                }
            }
        }

        return errors;
    };

    self.__defineGetter__("prefNames", function () {
        let ret = [];

        [self.prefDomain, self.prefDomain2].forEach(function (branch) {
            ret = ret.concat(services.get("pref").getBranch(branch).getChildList("", {}).map(function (pref) branch + pref));
        });

        return ret;
    });
    //}}}

    return self;

} //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>Add a short option for :sanitize -timespan.<commit_after>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

// TODO:
//   - should all storage items with the privateData flag be sanitizeable? i.e.
//     local-marks, url-marks, quick-marks, macros. Bookmarks et al aren't
//     sanitizeable in FF.
//   - add warning for TIMESPAN_EVERYTHING?
//   - respect privacy.clearOnShutdown et al or recommend VimperatorLeave autocommand?
//   - add support for :set sanitizeitems=all like 'eventignore'?
//   - integrate with the Clear Private Data dialog?

// FIXME:
//   - finish FF 3.0 support if we're going to support that in Vimperator 2.2

function Sanitizer() //{{{
{
    ////////////////////////////////////////////////////////////////////////////////
    ////////////////////// PRIVATE SECTION /////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    const local = {}; // XXX: is there some reason liberator.loadModule doesn't create modules with new?
    services.get("subscriptLoader").loadSubScript("chrome://browser/content/sanitize.js", local);
    const Sanitizer = local.Sanitizer;

    var prefToArgMap = {
        cache: "cache",
        cookies: "cookies",
        offlineApps: "offlineapps",
        history: "history",
        formdata: "formdata",
        downloads: "downloads",
        passwords: "passwords",
        sessions: "sessions",
        siteSettings: "sitesettings",
        commandLine: "commandline"
    };

    function prefToArg(pref) prefToArgMap[pref.replace(/.*\./, "")]
    function argToPref(arg) [p for ([p, a] in Iterator(prefToArgMap)) if (a == arg)][0]

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// OPTIONS /////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    options.add(["sanitizeitems", "si"],
        "The default list of private items to sanitize",
        "stringlist", "history,formdata,cookies,cache,sessions,commandline",
        {
            setter: function (values)
            {
                for (let [, pref] in Iterator(sanitizer.prefNames))
                {
                    options.setPref(pref, false);

                    for (let [, value] in Iterator(values.split(",")))
                    {
                        if (prefToArg(pref) == value)
                        {
                            options.setPref(pref, true);
                            break;
                        }
                    }
                }

                return values;
            },
            getter: function () sanitizer.prefNames.filter(function (pref) options.getPref(pref)).map(prefToArg).join(","),
            completer: function (value) [
                ["cache", "Cache"],
                ["cookies", "Cookies"],
                ["offlineapps", "Offline website data"],
                ["history", "Browsing history"],
                ["formdata", "Saved form and search history"],
                ["downloads", "Download history"],
                ["passwords", "Saved passwords"],
                ["sessions", "Authenticated sessions"],
                ["sitesettings", "Site preferences"],
                ["commandline", "Command-line history"]
            ],
            validator: Option.validateCompleter
        });

    options.add(["sanitizetimespan", "sts"],
        "The default sanitizer time span",
        "number", 1,
        {
            setter: function (value)
            {
                options.setPref("privacy.sanitize.timeSpan", value);
                return value;
            },
            getter: function () options.getPref("privacy.sanitize.timeSpan", this.defaultValue),
            completer: function (value) [
                ["0", "Everything"],
                ["1", "Last hour"],
                ["2", "Last two hours"],
                ["3", "Last four hours"],
                ["4", "Today"]
            ],
            validator: Option.validateCompleter
        });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// COMMANDS ////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    commands.add(["sa[nitize]"],
        "Clear private data",
        function (args)
        {
            if (options['private'])
                return void liberator.echomsg("Cannot sanitize items in private mode");

            let timespan = args["-timespan"] || options["sanitizetimespan"];

            sanitizer.range = Sanitizer.getClearRange(timespan);
            sanitizer.ignoreTimespan = !sanitizer.range;

            if (args.bang)
            {
                if (args.length > 0)
                    return void liberator.echoerr("E488: Trailing characters");

                liberator.log("Sanitizing all items in 'sanitizeitems'...");

                let errors = sanitizer.sanitize();

                if (errors)
                {
                    for (let item in errors)
                        liberator.echoerr("Error sanitizing " + item + ": " + errors[item]);
                }
            }
            else
            {
                if (args.length == 0)
                    return void liberator.echoerr("E471: Argument required");

                for (let [, item] in Iterator(args.map(argToPref)))
                {
                    liberator.log("Sanitizing " + item + " items...");

                    if (sanitizer.canClearItem(item))
                    {
                        try
                        {
                            sanitizer.clearItem(item);
                        }
                        catch (e)
                        {
                            liberator.echoerr("Error sanitizing " + item + ": " + e);
                        }
                    }
                    else
                        liberator.echomsg("Cannot sanitize " + item);
                }
            }
        },
        {
            argCount: "*", // FIXME: should be + and 0
            bang: true,
            completer: function (context) {
                context.title = ["Privacy Item", "Description"];
                context.completions = options.get("sanitizeitems").completer();
            },
            options: [
                [["-timespan", "-t"],
                 commands.OPTION_INT,
                 function (arg) /^[0-4]$/.test(arg),
                 function () options.get("sanitizetimespan").completer()]
             ]
        });

    /////////////////////////////////////////////////////////////////////////////}}}
    ////////////////////// PUBLIC SECTION //////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////{{{

    var self = new Sanitizer();

    // TODO: remove this version test
    if (/^1.9.1/.test(services.get("xulAppInfo").platformVersion))
        self.prefDomain = "privacy.cpd.";
    else
        self.prefDomain = "privacy.item.";

    self.prefDomain2 = "extensions.liberator.privacy.cpd.";
    self.items.commandLine = {
        canClear: true,
        clear: function ()
        {
            let stores = ["command", "search"];

            if (self.range)
            {
                stores.forEach(function (store) {
                    storage["history-" + store].mutate("filter", function (item) {
                        let timestamp = item.timestamp * 1000;
                        return timestamp < self.range[0] || timestamp > self.range[1];
                    });
                });
            }
            else
                stores.forEach(function (store) { storage["history-" + store].truncate(0); });
        }
    };

    // FIXME
    // create liberator-specific sanitize prefs
    if (options.getPref(self.prefDomain2 + "commandLine") == null)
        options.setPref(self.prefDomain2 + "commandLine", false)

    self.getClearRange = Sanitizer.getClearRange;

    // Largely ripped from from browser/base/content/sanitize.js so we can override
    // the pref strategy without stepping on the global prefs namespace.
    self.sanitize = function () {
        const prefService = services.get("pref");
        let branch = prefService.getBranch(this.prefDomain);
        let branch2 = prefService.getBranch(this.prefDomain2);
        let errors = null;

        function prefSet(name)
        {
            try
            {
                return branch.getBoolPref(name);
            }
            catch (e)
            {
                return branch2.getBoolPref(name);
            }
        }

        // Cache the range of times to clear
        if (this.ignoreTimespan)
            var range = null;  // If we ignore timespan, clear everything
        else
            range = this.range || Sanitizer.getClearRange();

        for (let itemName in this.items)
        {
            let item = this.items[itemName];
            item.range = range;

            if ("clear" in item && item.canClear && prefSet(itemName))
            {
                liberator.log("Sanitizing " + itemName + " items...");
                // Some of these clear() may raise exceptions (see bug #265028)
                // to sanitize as much as possible, we catch and store them,
                // rather than fail fast.
                // Callers should check returned errors and give user feedback
                // about items that could not be sanitized
                try
                {
                    item.clear();
                }
                catch (e)
                {
                    if (!errors)
                        errors = {};
                    errors[itemName] = e;
                    dump("Error sanitizing " + itemName + ": " + e + "\n");
                }
            }
        }

        return errors;
    };

    self.__defineGetter__("prefNames", function () {
        let ret = [];

        [self.prefDomain, self.prefDomain2].forEach(function (branch) {
            ret = ret.concat(services.get("pref").getBranch(branch).getChildList("", {}).map(function (pref) branch + pref));
        });

        return ret;
    });
    //}}}

    return self;

} //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>/**
 * Copyright IBM Corp. 2019, 2019
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

const cli = require('yargs');
const packageJson = require('../package.json');
const { UpgradeError } = require('./error');
const { reporter } = require('./reporter');
const { run, runInDirectory } = require('./runner');

async function main({ argv, cwd }) {
  cli.scriptName(packageJson.name).version(packageJson.version);

  cli
    .option('verbose', {
      default: false,
      describe: 'display the full output while running a command',
    })
    .option('dry', {
      alias: 'd',
      describe:
        'view the result of running this command without changing any files',
      default: false,
    })
    .option('ignore', {
      alias: 'i',
      describe: 'provide a glob pattern for directories you would like ignored',
      default: '',
    });

  cli
    .usage('Usage: $0 [options]')
    .command('$0', 'run to upgrade your project', {}, async args => {
      const { dry, ignore, verbose } = args;
      const options = {
        cwd: cwd(),
        dry,
        ignore,
        verbose,
      };

      await runCommand(() => runInDirectory(options), options);
    });

  cli.command(
    'migrate <package> <from> <to>',
    'run a specific migration for a package',
    {},
    async args => {
      const { dry, from, ignore, package: packageName, to, verbose } = args;
      const options = {
        cwd: cwd(),
        dry,
        ignore,
        verbose,
      };

      await runCommand(() => run(packageName, from, to, options), options);
    }
  );

  cli
    .demandCommand()
    .recommendCommands()
    .strict()
    .parse(argv.slice(2)).argv;
}

async function runCommand(makePromise, options) {
  reporter.info('Thanks for trying out carbon-upgrade! 🙏');
  reporter.info(
    'To help prevent any accidental changes, make sure to check in your ' +
      'work in version control first and use dry mode (-d flag) to ' +
      'preview any updates!'
  );

  if (options.verbose) {
    reporter.setLogLevel('verbose');
  }

  try {
    const start = Date.now();
    await makePromise();
    console.log(`✨ Done in ${Date.now() - start}ms`);
  } catch (error) {
    if (error instanceof UpgradeError) {
      reporter.error(error.message);
      return;
    }
    reporter.error('Yikes, looks like something really went wrong.');
    reporter.error('Please make an issue with the following info:');
    console.log(error);
  }
}

module.exports = main;
<commit_msg>fix(cli): revert change to log out time to complete<commit_after>/**
 * Copyright IBM Corp. 2019, 2019
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';

const cli = require('yargs');
const packageJson = require('../package.json');
const { UpgradeError } = require('./error');
const { reporter } = require('./reporter');
const { run, runInDirectory } = require('./runner');

async function main({ argv, cwd }) {
  cli.scriptName(packageJson.name).version(packageJson.version);

  cli
    .option('verbose', {
      default: false,
      describe: 'display the full output while running a command',
    })
    .option('dry', {
      alias: 'd',
      describe:
        'view the result of running this command without changing any files',
      default: false,
    })
    .option('ignore', {
      alias: 'i',
      describe: 'provide a glob pattern for directories you would like ignored',
      default: '',
    });

  cli
    .usage('Usage: $0 [options]')
    .command('$0', 'run to upgrade your project', {}, async args => {
      const { dry, ignore, verbose } = args;
      const options = {
        cwd: cwd(),
        dry,
        ignore,
        verbose,
      };

      await runCommand(() => runInDirectory(options), options);
    });

  cli.command(
    'migrate <package> <from> <to>',
    'run a specific migration for a package',
    {},
    async args => {
      const { dry, from, ignore, package: packageName, to, verbose } = args;
      const options = {
        cwd: cwd(),
        dry,
        ignore,
        verbose,
      };

      await runCommand(() => run(packageName, from, to, options), options);
    }
  );

  cli
    .demandCommand()
    .recommendCommands()
    .strict()
    .parse(argv.slice(2)).argv;
}

async function runCommand(makePromise, options) {
  reporter.info('Thanks for trying out carbon-upgrade! 🙏');
  reporter.info(
    'To help prevent any accidental changes, make sure to check in your ' +
      'work in version control first and use dry mode (-d flag) to ' +
      'preview any updates!'
  );

  if (options.verbose) {
    reporter.setLogLevel('verbose');
  }

  try {
    await makePromise();
    console.log('Done! ✨');
  } catch (error) {
    if (error instanceof UpgradeError) {
      reporter.error(error.message);
      return;
    }
    reporter.error('Yikes, looks like something really went wrong.');
    reporter.error('Please make an issue with the following info:');
    console.log(error);
  }
}

module.exports = main;
<|endoftext|>