<commit_before>(function(L) {
  'use strict';

  L.views.NoteView = Backbone.View.extend({
    tagName: 'li',
    className: 'note hbox justified_box',
    initialize: function(options) {
      var that = this;
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
        that.closeEditor();
      });
      this.template = L.templates["note"];
      this.listenTo(this.model, 'change:contents', _.mask(this.updateContents, 2));
      this.listenTo(this.model, 'change:meta', _.mask(this.updateMeta, 2));
    },
    remove : function(options) {
      if (this._rendered) {
        this.closeEditor();
        this.cleanupEditor();
        var el = this.$el;
        if (options && options.animate) {
          el.stop().fadeOut({queue: false}, 200).slideUp(300, function() {
            el.remove();
          });
        } else {
          el.remove();
        }
      }
    },
    isVisible: function() {
      // This assumes that el having a parent means that it is visible. Much
      // faster than checking $el.is(':visible') and speed is important here.
      return this.el && this.el.parentNode;
    },
    render: function(options) {
      this.$el.prop("id", "note-"+this.model.id);
      this.$el.attr("data-note", this.model.id);

      if (this._rendered) {
        this.closeEditor();
        this.undelegateEvents();
        this.delegateEvents();
      } else {
        this._rendered = true;
        this.$el.html(this.template(this.model.toJSON()));
        this.updateMeta();
      }

      return this;
    },
    cleanupEditor: function() {
      if (this.editor) {
        this.editor.remove();
        delete this.editor;
      }
    },
    updateMeta: function(options) {
      this.$el.prop('className', this.className);
      if (this.model.get('meta', {}).pinned) {
        this.$el.addClass('pinned');
      } else {
        this.$el.removeClass('pinned');
      }
    },
    updateContents: function(options) {
      this.$el.children('.contents').html(this.model.get('contents'));
    },
    events: {
      'click     .close-btn':             'onRemoveClicked',
      'click     .contents':              'onClick',
      'click     .contents a':            'onLinkOpen',
      'click     .contents .listit_tag':  'onTagClick',
      'keyup     .contents':              'onKeyUp',
      'blur      .editor':                'onBlur',
      'keydown   .editor':                'onKeyDown',
      'click     .pin-icon':              'onPinToggle',
      'resize    .editor':                'onResizeEditor',
      'mousedown .pin-icon':              function(event){event.preventDefault();}
    },
    onLinkOpen: function(event) {
      this.model.trigger('user:open-bookmark', this.model, this, event.target.href);
      event.stopPropagation();
    },
    onRemoveClicked: function() {
      L.notebook.trashNote(this.model);
      return false;
    },
    onResizeEditor: function() {
      this.$el.scrollIntoView();
    },
    onTagClick: function(event) {
      L.omnibox.tagToggle(event.target.textContent);
      return false;
    },
    expand: function() {
      this.$el.css('height', 'auto');
    },
    collapse: function() {
      this.$el.css('height', '');
    },
    openEditor: function() {
      // I know that this is unreadable. But it makes a point.
      // (it's complicated)

      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        return; // Already open
      }
      if (!this.editor) {
        this.editor = new ListIt.views.Editor({
          text: this.model.get('contents')
        });
        $editorEl.html(this.editor.render().el);
      } else {
        this.editor.setText(this.model.get('contents'));
      }
      $editorEl.show();
      $contentsEl.hide();
      this.$el.trigger('startediting');
      this.editor.focus();
      this.model.trigger('user:edit', this.model, this);
    },
    onBlur: function() {
      var that = this;
      if (!this.editor || this.editor.isShowingDialog()) {
        return;
      }
      _.defer(function() {
        var focusedEditor = $(document.activeElement).closest('.editor').get(0),
            myEditor = that.$('.editor').get(0);

        if (focusedEditor !== myEditor) {
          that.closeEditor();
        }
      });
    },
    closeEditor: function() {
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.storeText();
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },
    onClick: function(e) {
      this.expand();
      this.openEditor();
    },
    /**
     * Scroll List so note selected with tab is at top.
     **/
    onKeyUp: function(event) {
      if (event.keyCode === KeyCode.TAB) {
        this.$el.scrollIntoView();
        $('#notes')[0].scrollTop -= 4;
      }
    },
    onKeyDown: function(event) {
      if ((
        event.keyCode === KeyCode.ESC ||
        (event.keyCode === KeyCode.ENTER && event.shiftKey) ||
        (event.which === KeyCode.S && event.ctrlKey)
        ) && this.editor && !this.editor.isShowingDialog()) {
        event.preventDefault();
        this.closeEditor();
      }
    },
    storeText: function() {
      this.model.changeContents(this.editor.getText(), window);
      this.model.trigger('user:save', this.model, this);
    },
    //if editor is visible, gets/sets text through editor - otherwise, through model
    onPinToggle: function(event) {
      if (this.$('.editor-container').is(":visible")) {
        if (L.util.strip(this.editor.getText())[0] === '!') {
          this.editor.replaceText(/!+ */, '');
          this.$el.removeClass('pinned');
        } else {
          this.editor.prependText('! ');
          this.$el.addClass('pinned');
        }
      } else {
        var text = this.model.get('contents');
        if (this.model.get('meta').pinned) {
          text = text.replace(/!+ */, '');
        } else {
          text = '! ' + text;
        }
        this.model.changeContents(text);
      }
    }
    /*onCancel: function(event){
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },*/
  });

  L.views.NoteCollectionView = Backbone.View.extend({
    defaults: {
      noteHeight: '1.3em', //L.base.oneLineHeight, //'1.2em',
      lastFocusedNote: null
    },
    initialize: function() {
      var that = this;
      this.subViews = {}; // Note views
      this.renderNext = 0;
      this.listenTo(this.collection, 'add', _.mask(this.addNote, 0));
      this.listenTo(this.collection, 'remove', function(note, col, options) {
        that.removeNote(note, _.defaults({}, options));
      });
      this.listenTo(this.collection, 'reset', _.mask(this.reset, 1));
      this.listenTo(this.collection, 'sort', _.mask(this.sort));
      this.listenTo(L.preferences, 'change:shrinkNotes', this.updateNoteShrinkState);
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
      });
      // Load more if needed on window resize.
      $(window).on("resize", function() {
        if (that._rendered) {
          that.lazyLoadMore();
        }
      });
    },
    events: {
      'scroll': 'onScroll',
      'stopediting .note': 'onStopEditing',
      'startediting .note': 'onStartEditing'
    },
    onStartEditing: function() {
      this.$('#notes-container').sortable("disable");
    },
    onStopEditing: function() {
      this.$('#notes-container').sortable("enable");
    },
    updateNoteShrinkState : function(model, state) {
      if (!this._rendered) {
        return;
      }

      if (state) {
        this.$el.addClass('shrink');
      } else {
        this.$el.removeClass('shrink');
      }
    },
    checkLoadMore: function() {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          listHeight = this.$el.children('#notes-container').height();

      return (scrollTop + 2*containerHeight) > listHeight;
    },
    onScroll: function() {
      // Load on scroll.
      this.loadMore();
    },
    loadMore: function() {
      var view, note;
      while (this.collection.size() > this.renderNext && this.shouldRenderAt(this.renderNext)) {
        note = this.collection.at(this.renderNext);
        view = this.subViews[note.id];
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        if (!view.isVisible()) {
          this.insertAt(this.renderNext, view);
        }
        this.renderNext += 1;
      }
    },
    lazyLoadMore: _.debounce(function() {
      return this.loadMore();
    }, 100),
    shouldRenderAt: function(index) {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          notes = this.$('#notes-container .note'),
          noteOffset;
      if (notes.length > index) {
        noteOffset = notes.eq(index).position().top;
      } else if (notes.length > 0) {
        noteOffset = notes.last().position().top;
      } else {
        noteOffset = 0;
      }
      return (scrollTop + 2*containerHeight) > noteOffset;
    },
    addNote: function(note, idx) {
      var view = this.subViews[note.id];

      // Ignore already visible/unrendered
      if (!this._rendered || view && view.isVisible()) {
        return true;
      }

      // Get the index but allow it to be specified manually.
      var index = (typeof(idx) === "number") ? idx : this.collection.indexOf(note);

      if (this.shouldRenderAt(index)) {
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        this.insertAt(index, view);
        if (index < this.renderNext) {
          this.renderNext += 1;
        }
        this.fixHeight();
        return true;
      } else if (index < this.renderNext) {
        // If I choose not to render, fix the render next index.
        this.renderNext = index;
        return false;
      }
    },
    insertAt: function(index, view) {
      var otherEl = this.$el.children('#notes-container').find('.note').eq(index);
      if (otherEl.length === 0) {
        this.$el.children('#notes-container').append(view.render().$el);
      } else {
        otherEl.before(view.render().$el);
      }
    },
    // Delay removes just in case we are re-adding the note.
    removeNote: function(note, options) {
      var id = note.id || note,
          view = this.subViews[id];

      this.fixHeight();
      if (!view || !this._rendered) {
        return;
      }

      if (0 < this.$('#notes-container .note').index(view.$el) < this.renderNext) {
        this.renderNext--;
      }

      view.remove(options);
      this.lazyLoadMore();
    },
    fixHeight: _.throttle(function() {
      this.$('#notes-container').css("min-height", 36*this.collection.size());
    }, 100),
    sort: _.debounce(function() {
      var that = this;
      this.collection.each(function(note, i) {
        var view = that.subViews[note.id];
        if (view) {
          view.remove();
          that.insertAt(i, view);
        }
      });
    }, 100),
    reset: function() {
      this.renderNext = 0;
      if (this._rendered) {
        this.$el.children('#notes-container').empty();
        // Bail early.
        this.collection.every(_.mask(_.bind(this.addNote, this), 0, 1));
      }
    },
    render: function() {
      if (!this._rendered) {
        var ul = $('<ul>');
        ul.prop({ className: 'notelist', id: 'notes-container' });
        this.$el.html(ul);

        var collection = this.collection.backingCollection;

        // Prevent clicks from getting triggered when sorting.
        var sorting = false;
        ul.get(0).addEventListener("click", function(e) {
          if (sorting) {
            e.stopImmediatePropagation();
          }
        }, true);

        var current;
        ul.sortable({
          distance: 10,
          items: '.note:not(.pinned)',
          containment: 'parent',
          tolerance: 'intersect',
          revert: 100,
          start: function(event, ui) {
            sorting = true;
            ui.placeholder.height(ui.item.height() / 2);
            current = ui.placeholder.next();
          },
          change: function(event, ui) {
            current.css('margin-top', '2px');
            current = ui.placeholder.next();
            current.css('margin-top', ui.placeholder.height());
          },
          stop: function(event, ui) {
            current.css({'-webkit-transition': 'none',
                         '-moz-transition': 'none',
                         'transition': 'none',
                        'margin-top': '2px'});
            var noteId = ui.item.attr('data-note');
            var previousId = ui.item.prev().attr('data-note');
            var note = collection.get(noteId);
            collection.remove(note);
            var insertIndex;
            if (previousId) {
              insertIndex = collection.indexOf(collection.get(previousId))+1;
            } else {
              insertIndex = 0;
            }
            collection.add(note, {at: insertIndex});

            // This needs to be set after the click event has been triggered.
            _.defer(function() { sorting = false; });
          }
        });

        this._rendered = true;
        this.updateNoteShrinkState(L.preferences, L.preferences.get('shrinkNotes'));
      }
      _.defer(_.bind(this.reset, this));
      return this;
    }
  });

})(ListIt);
<commit_msg>Save editor open state.<commit_after>(function(L) {
  'use strict';

  L.views.NoteView = Backbone.View.extend({
    tagName: 'li',
    className: 'note hbox justified_box',
    initialize: function(options) {
      var that = this;
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
        that.closeEditor();
      });
      this.template = L.templates["note"];
      this.listenTo(this.model, 'change:contents', _.mask(this.updateContents, 2));
      this.listenTo(this.model, 'change:meta', _.mask(this.updateMeta, 2));
    },
    remove : function(options) {
      if (this._rendered) {
        this.closeEditor();
        this.cleanupEditor();
        var el = this.$el;
        if (options && options.animate) {
          el.stop().fadeOut({queue: false}, 200).slideUp(300, function() {
            el.remove();
          });
        } else {
          el.remove();
        }
      }
    },
    isVisible: function() {
      // This assumes that el having a parent means that it is visible. Much
      // faster than checking $el.is(':visible') and speed is important here.
      return this.el && this.el.parentNode;
    },
    render: function(options) {
      this.$el.prop("id", "note-"+this.model.id);
      this.$el.attr("data-note", this.model.id);

      if (this._rendered) {
        this.closeEditor();
        this.undelegateEvents();
        this.delegateEvents();
      } else {
        this._rendered = true;
        this.$el.html(this.template(this.model.toJSON()));
        this.updateMeta();
      }

      return this;
    },
    cleanupEditor: function() {
      if (this.editor) {
        this.editor.remove();
        delete this.editor;
      }
    },
    updateMeta: function(options) {
      this.$el.prop('className', this.className);
      if (this.model.get('meta', {}).pinned) {
        this.$el.addClass('pinned');
      } else {
        this.$el.removeClass('pinned');
      }
    },
    updateContents: function(options) {
      this.$el.children('.contents').html(this.model.get('contents'));
    },
    events: {
      'click     .close-btn':             'onRemoveClicked',
      'click     .contents':              'onClick',
      'click     .contents a':            'onLinkOpen',
      'click     .contents .listit_tag':  'onTagClick',
      'keyup     .contents':              'onKeyUp',
      'blur      .editor':                'onBlur',
      'keydown   .editor':                'onKeyDown',
      'click     .pin-icon':              'onPinToggle',
      'resize    .editor':                'onResizeEditor',
      'mousedown .pin-icon':              function(event){event.preventDefault();}
    },
    onLinkOpen: function(event) {
      this.model.trigger('user:open-bookmark', this.model, this, event.target.href);
      event.stopPropagation();
    },
    onRemoveClicked: function() {
      L.notebook.trashNote(this.model);
      return false;
    },
    onResizeEditor: function() {
      this.$el.scrollIntoView();
    },
    onTagClick: function(event) {
      L.omnibox.tagToggle(event.target.textContent);
      return false;
    },
    expand: function() {
      this.$el.css('height', 'auto');
    },
    collapse: function() {
      this.$el.css('height', '');
    },
    openEditor: function() {
      // I know that this is unreadable. But it makes a point.
      // (it's complicated)

      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');

      this._editorOpen = true;
      if ($editorEl.is(":visible")) {
        return; // Already open
      }
      if (!this.editor) {
        this.editor = new ListIt.views.Editor({
          text: this.model.get('contents')
        });
        $editorEl.html(this.editor.render().el);
      } else {
        this.editor.setText(this.model.get('contents'));
      }
      $editorEl.show();
      $contentsEl.hide();
      this.$el.trigger('startediting');
      this.editor.focus();
      this.model.trigger('user:edit', this.model, this);
    },
    onBlur: function() {
      var that = this;
      if (!this.editor || this.editor.isShowingDialog()) {
        return;
      }
      _.defer(function() {
        var focusedEditor = $(document.activeElement).closest('.editor').get(0),
            myEditor = that.$('.editor').get(0);

        if (focusedEditor !== myEditor) {
          that.closeEditor();
        }
      });
    },
    closeEditor: function() {
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if (this._editorOpen) {
        this._editorOpen = false;
        this.storeText();
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },
    onClick: function(e) {
      this.expand();
      this.openEditor();
    },
    /**
     * Scroll List so note selected with tab is at top.
     **/
    onKeyUp: function(event) {
      if (event.keyCode === KeyCode.TAB) {
        this.$el.scrollIntoView();
        $('#notes')[0].scrollTop -= 4;
      }
    },
    onKeyDown: function(event) {
      if ((
        event.keyCode === KeyCode.ESC ||
        (event.keyCode === KeyCode.ENTER && event.shiftKey) ||
        (event.which === KeyCode.S && event.ctrlKey)
        ) && this.editor && !this.editor.isShowingDialog()) {
        event.preventDefault();
        this.closeEditor();
      }
    },
    storeText: function() {
      this.model.changeContents(this.editor.getText(), window);
      this.model.trigger('user:save', this.model, this);
    },
    //if editor is visible, gets/sets text through editor - otherwise, through model
    onPinToggle: function(event) {
      if (this.$('.editor-container').is(":visible")) {
        if (L.util.strip(this.editor.getText())[0] === '!') {
          this.editor.replaceText(/!+ */, '');
          this.$el.removeClass('pinned');
        } else {
          this.editor.prependText('! ');
          this.$el.addClass('pinned');
        }
      } else {
        var text = this.model.get('contents');
        if (this.model.get('meta').pinned) {
          text = text.replace(/!+ */, '');
        } else {
          text = '! ' + text;
        }
        this.model.changeContents(text);
      }
    }
    /*onCancel: function(event){
      var $contentsEl = this.$('.contents'),
          $editorEl = this.$('.editor-container');
      if ($editorEl.is(":visible")) {
        this.collapse();
        $editorEl.hide();
        this.$el.trigger('stopediting');
      }
      // Always show contents.
      $contentsEl.show();
    },*/
  });

  L.views.NoteCollectionView = Backbone.View.extend({
    defaults: {
      noteHeight: '1.3em', //L.base.oneLineHeight, //'1.2em',
      lastFocusedNote: null
    },
    initialize: function() {
      var that = this;
      this.subViews = {}; // Note views
      this.renderNext = 0;
      this.listenTo(this.collection, 'add', _.mask(this.addNote, 0));
      this.listenTo(this.collection, 'remove', function(note, col, options) {
        that.removeNote(note, _.defaults({}, options));
      });
      this.listenTo(this.collection, 'reset', _.mask(this.reset, 1));
      this.listenTo(this.collection, 'sort', _.mask(this.sort));
      this.listenTo(L.preferences, 'change:shrinkNotes', this.updateNoteShrinkState);
      $(window).one('beforeunload', function() {
        that.undelegateEvents();
        that.stopListening();
      });
      // Load more if needed on window resize.
      $(window).on("resize", function() {
        if (that._rendered) {
          that.lazyLoadMore();
        }
      });
    },
    events: {
      'scroll': 'onScroll',
      'stopediting .note': 'onStopEditing',
      'startediting .note': 'onStartEditing'
    },
    onStartEditing: function() {
      this.$('#notes-container').sortable("disable");
    },
    onStopEditing: function() {
      this.$('#notes-container').sortable("enable");
    },
    updateNoteShrinkState : function(model, state) {
      if (!this._rendered) {
        return;
      }

      if (state) {
        this.$el.addClass('shrink');
      } else {
        this.$el.removeClass('shrink');
      }
    },
    checkLoadMore: function() {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          listHeight = this.$el.children('#notes-container').height();

      return (scrollTop + 2*containerHeight) > listHeight;
    },
    onScroll: function() {
      // Load on scroll.
      this.loadMore();
    },
    loadMore: function() {
      var view, note;
      while (this.collection.size() > this.renderNext && this.shouldRenderAt(this.renderNext)) {
        note = this.collection.at(this.renderNext);
        view = this.subViews[note.id];
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        if (!view.isVisible()) {
          this.insertAt(this.renderNext, view);
        }
        this.renderNext += 1;
      }
    },
    lazyLoadMore: _.debounce(function() {
      return this.loadMore();
    }, 100),
    shouldRenderAt: function(index) {
      var scrollTop = this.$el.scrollTop(),
          containerHeight = this.$el.parent().height(),
          notes = this.$('#notes-container .note'),
          noteOffset;
      if (notes.length > index) {
        noteOffset = notes.eq(index).position().top;
      } else if (notes.length > 0) {
        noteOffset = notes.last().position().top;
      } else {
        noteOffset = 0;
      }
      return (scrollTop + 2*containerHeight) > noteOffset;
    },
    addNote: function(note, idx) {
      var view = this.subViews[note.id];

      // Ignore already visible/unrendered
      if (!this._rendered || view && view.isVisible()) {
        return true;
      }

      // Get the index but allow it to be specified manually.
      var index = (typeof(idx) === "number") ? idx : this.collection.indexOf(note);

      if (this.shouldRenderAt(index)) {
        if (!view) {
          view = this.subViews[note.id] = new L.views.NoteView({model: note});
        }
        this.insertAt(index, view);
        if (index < this.renderNext) {
          this.renderNext += 1;
        }
        this.fixHeight();
        return true;
      } else if (index < this.renderNext) {
        // If I choose not to render, fix the render next index.
        this.renderNext = index;
        return false;
      }
    },
    insertAt: function(index, view) {
      var otherEl = this.$el.children('#notes-container').find('.note').eq(index);
      if (otherEl.length === 0) {
        this.$el.children('#notes-container').append(view.render().$el);
      } else {
        otherEl.before(view.render().$el);
      }
    },
    // Delay removes just in case we are re-adding the note.
    removeNote: function(note, options) {
      var id = note.id || note,
          view = this.subViews[id];

      this.fixHeight();
      if (!view || !this._rendered) {
        return;
      }

      if (0 < this.$('#notes-container .note').index(view.$el) < this.renderNext) {
        this.renderNext--;
      }

      view.remove(options);
      this.lazyLoadMore();
    },
    fixHeight: _.throttle(function() {
      this.$('#notes-container').css("min-height", 36*this.collection.size());
    }, 100),
    sort: _.debounce(function() {
      var that = this;
      this.collection.each(function(note, i) {
        var view = that.subViews[note.id];
        if (view) {
          view.remove();
          that.insertAt(i, view);
        }
      });
    }, 100),
    reset: function() {
      this.renderNext = 0;
      if (this._rendered) {
        this.$el.children('#notes-container').empty();
        // Bail early.
        this.collection.every(_.mask(_.bind(this.addNote, this), 0, 1));
      }
    },
    render: function() {
      if (!this._rendered) {
        var ul = $('<ul>');
        ul.prop({ className: 'notelist', id: 'notes-container' });
        this.$el.html(ul);

        var collection = this.collection.backingCollection;

        // Prevent clicks from getting triggered when sorting.
        var sorting = false;
        ul.get(0).addEventListener("click", function(e) {
          if (sorting) {
            e.stopImmediatePropagation();
          }
        }, true);

        var current;
        ul.sortable({
          distance: 10,
          items: '.note:not(.pinned)',
          containment: 'parent',
          tolerance: 'intersect',
          revert: 100,
          start: function(event, ui) {
            sorting = true;
            ui.placeholder.height(ui.item.height() / 2);
            current = ui.placeholder.next();
          },
          change: function(event, ui) {
            current.css('margin-top', '2px');
            current = ui.placeholder.next();
            current.css('margin-top', ui.placeholder.height());
          },
          stop: function(event, ui) {
            current.css({'-webkit-transition': 'none',
                         '-moz-transition': 'none',
                         'transition': 'none',
                        'margin-top': '2px'});
            var noteId = ui.item.attr('data-note');
            var previousId = ui.item.prev().attr('data-note');
            var note = collection.get(noteId);
            collection.remove(note);
            var insertIndex;
            if (previousId) {
              insertIndex = collection.indexOf(collection.get(previousId))+1;
            } else {
              insertIndex = 0;
            }
            collection.add(note, {at: insertIndex});

            // This needs to be set after the click event has been triggered.
            _.defer(function() { sorting = false; });
          }
        });

        this._rendered = true;
        this.updateNoteShrinkState(L.preferences, L.preferences.get('shrinkNotes'));
      }
      _.defer(_.bind(this.reset, this));
      return this;
    }
  });

})(ListIt);
<|endoftext|>


<commit_before>'use strict';

const { addGenErrorHandler } = require('../error');
const { loadFile } = require('../formats');

// Load the file pointing to by the JSON reference
const load = function ({ path }) {
  return loadFile({ type: 'conf', path });
};

const eLoad = addGenErrorHandler(load, {
  message: ({ path }) => `Configuration file '${path}' is invalid: it does not exist or it has syntax errors`,
  reason: 'SCHEMA_VALIDATION',
});

module.exports = {
  load: eLoad,
};
<commit_msg>Better error messages<commit_after>'use strict';

const { pStat } = require('../utilities');
const { addGenErrorHandler } = require('../error');
const { loadFile } = require('../formats');

// Load the file pointing to by the JSON reference
const load = async function ({ path }) {
  await eStat(path);

  return eLoadFile({ type: 'conf', path });
};

const eLoadFile = addGenErrorHandler(loadFile, {
  message: ({ path }) => `Configuration file could not be parsed because it has syntax errors: '${path}'`,
  reason: 'SCHEMA_VALIDATION',
});

const eStat = addGenErrorHandler(pStat, {
  message: path => `Configuration file does not exist: '${path}'`,
  reason: 'SCHEMA_VALIDATION',
});

module.exports = {
  load,
};
<|endoftext|>


<commit_before>'use strict'

/**
 * Dependencies
 */
const KeyPair = require('./KeyPair')

/**
 * ECKeyPair
 */
class ECKeyPair extends KeyPair {

  /**
   * Generate
   */
  static generate () {
    return Promise.resolve('TBD')
  }
}

/**
 * Export
 */
module.exports = ECKeyPair
<commit_msg>feat(ECKeyPair): generate PEM-encoded Elliptic Curve key pair<commit_after>'use strict'

/**
 * Dependencies
 * @ignore
 */
const {spawn} = require('child_process')
const KeyPair = require('./KeyPair')

/**
 * ECKeyPair
 *
 * @class
 * ECKeyPair represents an Elliptic Curve cryptographic keypair in memory.
 * It maintains both PEM and JWK representations.
 */
class ECKeyPair extends KeyPair {

  /**
   * Generate
   */
  static generate () {
    return new Promise((resolve, reject) => {
      let keypair = {}
      let ecparam = spawn('openssl', ['ecparam', '-name', 'secp256k1', '-genkey'])
      let ec = spawn('openssl', ['ec', '-pubout'])

      ecparam.stdout.on('data', (data) => {
        keypair.prv = data.toString('ascii')
        ec.stdin.write(data)
      })

      ec.stdout.on('data', (data) => {
        keypair.pub = data.toString('ascii')
      })

      ec.on('close', (code) => {
        resolve(new ECKeyPair(keypair))
      })
    })
  }

  /**
   * Constructor
   */
  constructor (data) {
    super(data)
    Object.assign(this, data)
  }

}

/**
 * Export
 */
module.exports = ECKeyPair
<|endoftext|>


<commit_before>// .vuepress/config.js
let date = new Date().toJSON();
module.exports = {
  themeConfig: {
    sidebar: [
      "/",
      "/architecture.md",
      "/messages.md",
      "/status.md",
      "/enumeration.md",
      "/generic.md",
      "/specific.md",
      "/deprecated.md",
    ]
  },
  plugins: [
    [
      "vuepress-plugin-matomo",
      {
        'siteId': 7,
        'trackerUrl': "https://matomo.nonpolynomial.com/"
      }
    ],
    "@vuepress/plugin-back-to-top"
  ],
  evergreen: true,
  title: "Buttplug Protocol Specification",
a  description: "Specification for the Buttplug Intimate Hardware Control Protocol",
  head: [
    ['link', { rel: 'icon', href: '/buttplug.svg' }],
    ["meta", {property: "og:type", content:"website"}],
    ["meta", {property: "og:title", content:"Buttplug Protocol Specification"}],
    ["meta", {property: "og:url", content:"https://buttplug-spec.docs.buttplug.io"}],
    ["meta", {property: "og:site_name", content:"Buttplug Protocol Specification"}],
    ["meta", {property: "og:description", content:"Specification for the Buttplug Intimate Hardware Control Protocol."}],
    ["meta", {property: "og:locale", content:"default"}],
    ["meta", {property: "og:image", content:"https://buttplug-spec.docs.buttplug.io/buttplug-logo-opengraph.png"}],
    ["meta", {property: "og:updated_time", content:date}],
    ["meta", {name:"twitter:card", content:"summary"}],
    ["meta", {name:"twitter:title", content:"Buttplug Protocol Specification"}],
    ["meta", {name:"twitter:description", content:"Specification for the Buttplug Intimate Hardware Control Protocol."}],
    ["meta", {name:"twitter:image", content:"https://buttplug-spec.docs.buttplug.io/buttplug-logo-opengraph.png"}],
    ["meta", {name:"twitter:creator", content:"@buttplugio"}],
  ]
};
<commit_msg>Fix config typo<commit_after>// .vuepress/config.js
let date = new Date().toJSON();
module.exports = {
  themeConfig: {
    sidebar: [
      "/",
      "/architecture.md",
      "/messages.md",
      "/status.md",
      "/enumeration.md",
      "/generic.md",
      "/specific.md",
      "/deprecated.md",
    ]
  },
  plugins: [
    [
      "vuepress-plugin-matomo",
      {
        'siteId': 7,
        'trackerUrl': "https://matomo.nonpolynomial.com/"
      }
    ],
    "@vuepress/plugin-back-to-top"
  ],
  evergreen: true,
  title: "Buttplug Protocol Specification",
  description: "Specification for the Buttplug Intimate Hardware Control Protocol",
  head: [
    ['link', { rel: 'icon', href: '/buttplug.svg' }],
    ["meta", {property: "og:type", content:"website"}],
    ["meta", {property: "og:title", content:"Buttplug Protocol Specification"}],
    ["meta", {property: "og:url", content:"https://buttplug-spec.docs.buttplug.io"}],
    ["meta", {property: "og:site_name", content:"Buttplug Protocol Specification"}],
    ["meta", {property: "og:description", content:"Specification for the Buttplug Intimate Hardware Control Protocol."}],
    ["meta", {property: "og:locale", content:"default"}],
    ["meta", {property: "og:image", content:"https://buttplug-spec.docs.buttplug.io/buttplug-logo-opengraph.png"}],
    ["meta", {property: "og:updated_time", content:date}],
    ["meta", {name:"twitter:card", content:"summary"}],
    ["meta", {name:"twitter:title", content:"Buttplug Protocol Specification"}],
    ["meta", {name:"twitter:description", content:"Specification for the Buttplug Intimate Hardware Control Protocol."}],
    ["meta", {name:"twitter:image", content:"https://buttplug-spec.docs.buttplug.io/buttplug-logo-opengraph.png"}],
    ["meta", {name:"twitter:creator", content:"@buttplugio"}],
  ]
};
<|endoftext|>


<commit_before>/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;

/**
 * jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability).
 *
**/
jsl.format = (function () {

    function repeat(s, count) {
        return new Array(count + 1).join(s);
    }

    function formatJson(json, options) {
        options = options || {};
        var tabSize = options.tabSize || 2;
        var indentCStyle = options.indentCStyle || false;

        var tab = "";
        for (var ts = 0; ts < tabSize; ts++) {
          tab += " ";
        }

        var i           = 0,
            il          = 0,
            newJson     = "",
            indentLevel = 0,
            inString    = false,
            currentChar = null;

        for (i = 0, il = json.length; i < il; i += 1) {
            currentChar = json.charAt(i);

            switch (currentChar) {
            case '{':
            case '[':
                if (!inString) {
                    if (indentCStyle) newJson += "\n" + repeat(tab, indentLevel);
                    newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
                    indentLevel += 1;
                } else {
                    newJson += currentChar;
                }
                break;
            case '}':
            case ']':
                if (!inString) {
                    indentLevel -= 1;
                    newJson += "\n" + repeat(tab, indentLevel) + currentChar;
                } else {
                    newJson += currentChar;
                }
                break;
            case ',':
                if (!inString) {
                    newJson += ",\n" + repeat(tab, indentLevel);
                } else {
                    newJson += currentChar;
                }
                break;
            case ':':
                if (!inString) {
                    newJson += ": ";
                } else {
                    newJson += currentChar;
                }
                break;
            case ' ':
            case "\n":
            case "\t":
                if (inString) {
                    newJson += currentChar;
                }
                break;
            case '"':
                if (i > 0 && (json.charAt(i - 1) !== '\\' || (json.charAt(i - 1) == '\\' && json.charAt(i - 2) == '\\'))) {
                    inString = !inString;
                }
                newJson += currentChar;
                break;
            default:
                newJson += currentChar;
                break;
            }
        }

        return newJson;
    }

    return { "formatJson": formatJson };

}());

module.exports = jsl.format.formatJson;
<commit_msg>Preserve spaces in plain strings<commit_after>/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: false, bitwise: true, newcap: true, maxerr: 50, indent: 4 */
var jsl = typeof jsl === 'undefined' ? {} : jsl;

/**
 * jsl.format - Provide json reformatting in a character-by-character approach, so that even invalid JSON may be reformatted (to the best of its ability).
 *
**/
jsl.format = (function () {

    function repeat(s, count) {
        return new Array(count + 1).join(s);
    }

    function formatJson(json, options) {
        options = options || {};
        var tabSize = options.tabSize || 2;
        var indentCStyle = options.indentCStyle || false;

        var tab = "";
        for (var ts = 0; ts < tabSize; ts++) {
          tab += " ";
        }

        var i           = 0,
            il          = 0,
            newJson     = "",
            indentLevel = 0,
            inString    = false,
            currentChar = null;

        for (i = 0, il = json.length; i < il; i += 1) {
            currentChar = json.charAt(i);

            switch (currentChar) {
            case '{':
            case '[':
                if (!inString) {
                    if (indentCStyle) newJson += "\n" + repeat(tab, indentLevel);
                    newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
                    indentLevel += 1;
                } else {
                    newJson += currentChar;
                }
                break;
            case '}':
            case ']':
                if (!inString) {
                    indentLevel -= 1;
                    newJson += "\n" + repeat(tab, indentLevel) + currentChar;
                } else {
                    newJson += currentChar;
                }
                break;
            case ',':
                if (!inString) {
                    newJson += ",\n" + repeat(tab, indentLevel);
                } else {
                    newJson += currentChar;
                }
                break;
            case ':':
                if (!inString) {
                    newJson += ": ";
                } else {
                    newJson += currentChar;
                }
                break;
            case ' ':
            case "\n":
            case "\t":
                if (inString) {
                    newJson += currentChar;
                }
                break;
            case '"':
                if (i === 0) {
                    inString = true;
                }
                else if (json.charAt(i - 1) !== '\\' || (json.charAt(i - 1) == '\\' && json.charAt(i - 2) == '\\')) {
                    inString = !inString;
                }
                newJson += currentChar;
                break;
            default:
                newJson += currentChar;
                break;
            }
        }

        return newJson;
    }

    return { "formatJson": formatJson };

}());

module.exports = jsl.format.formatJson;
<|endoftext|>


<commit_before>import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { Users } from '/imports/api/user/UserCollection';
import { Opportunities } from '/imports/api/opportunity/OpportunityCollection';
import { Courses } from '/imports/api/course/CourseCollection';
import { Slugs } from '/imports/api/slug/SlugCollection';

import BaseInstanceCollection from '/imports/api/base/BaseInstanceCollection';

/** @module Feed */

/**
 * Represents a feed instance.
 * @extends module:BaseInstance~BaseInstanceCollection
 */
class FeedCollection extends BaseInstanceCollection {

  /**
   * Creates the Feed collection.
   */
  constructor() {
    super('Feed', new SimpleSchema({
      slugID: { type: SimpleSchema.RegEx.Id },
      studentID: { type: SimpleSchema.RegEx.Id, optional: true },
      opportunityID: { type: SimpleSchema.RegEx.Id, optional: true },
      courseID: { type: SimpleSchema.RegEx.Id, optional: true },
      description: { type: String },
      timestamp: { type: Number },
      picture: { type: String },
    }));
  }

  /**
   * Defines a new Feed.
   * @example
   * Feed.define({ student: 'abigailkealoha',
   *                 opportunity: 'att-hackathon
   *                 course: undefined
   *                 feedType: 'verified'
   *                 timestamp: '12345465465',
   * @param { Object } description Object with keys student, opportunity, course, feedType, and timestamp.
   * @returns The newly created docID.
   */
  define({ student, opportunity, course, feedType, timestamp }) {
    let studentID;
    let opportunityID;
    let courseID;
    let picture;
    let slugID;
    if (student) {
      studentID = Users.getID(student);
    }
    if (opportunity) {
      opportunityID = Opportunities.getID(opportunity);
    }
    if (course) {
      courseID = Courses.getID(course);
    }
    let description = '';
    if (feedType === 'new') {
      if (student !== undefined) {
        description = `${Users.getFullName(studentID)} has joined RadGrad.`;
        slugID = Slugs.define({ name: `feed-${Users.findDoc(studentID).username}-new`, entityName: this.getType() });
        picture = Users.findDoc(studentID).picture;
        if (!picture) {
          picture = '/images/ICS-logo.png';
        }
      } else if (opportunity !== undefined) {
        description = `${Opportunities.findDoc(opportunityID).name} has been added to Opportunities.`;
        slugID = Slugs.define({ name: `feed-${Slugs.getDoc(Opportunities.findDoc(opportunityID).slugID).name}-new`,
          entityName: this.getType() });
        picture = '/images/radgrad_logo.png';
      } else if (course !== undefined) {
        description = `${Courses.findDoc(courseID).name} has been added to Courses.`;
        slugID = Slugs.define({ name: `${Slugs.getDoc(Courses.findDoc(courseID).slugID).name}-new`,
          entityName: this.getType() });
        picture = '/images/radgrad_logo.png';
      }
    } else if (feedType === 'verified') {
      description = `${Users.getFullName(studentID)} has been verified for
        ${Opportunities.findDoc(opportunityID).name}.`;
      slugID = Slugs.define({ name: `feed-${Users.findDoc(studentID).username}-${Slugs.findDoc(Opportunities.findDoc(opportunityID).slugID).name}-new`, entityName: this.getType() });
      picture = Users.findDoc(studentID).picture;
    }
    const feedID = this._collection.insert({ slugID, studentID, opportunityID, courseID,
      description, timestamp, picture });
    Slugs.updateEntityID(slugID, feedID);
    return feedID;
  }
  getSlug(feedID) {
    this.assertDefined(feedID);
    const feedDoc = this.findDoc(feedID);
    return Slugs.findDoc(feedDoc.slugID).name;
  }
}

/**
 * Provides the singleton instance of this class to all other entities.
 */
export const Feed = new FeedCollection();

<commit_msg>Finished fixing feed slugs for verification events<commit_after>import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { Users } from '/imports/api/user/UserCollection';
import { Opportunities } from '/imports/api/opportunity/OpportunityCollection';
import { Courses } from '/imports/api/course/CourseCollection';
import { Semesters } from '/imports/api/semester/SemesterCollection';
import { Slugs } from '/imports/api/slug/SlugCollection';

import BaseInstanceCollection from '/imports/api/base/BaseInstanceCollection';

/** @module Feed */

/**
 * Represents a feed instance.
 * @extends module:BaseInstance~BaseInstanceCollection
 */
class FeedCollection extends BaseInstanceCollection {

  /**
   * Creates the Feed collection.
   */
  constructor() {
    super('Feed', new SimpleSchema({
      slugID: { type: SimpleSchema.RegEx.Id },
      studentID: { type: SimpleSchema.RegEx.Id, optional: true },
      opportunityID: { type: SimpleSchema.RegEx.Id, optional: true },
      courseID: { type: SimpleSchema.RegEx.Id, optional: true },
      description: { type: String },
      timestamp: { type: Number },
      picture: { type: String },
    }));
  }

  /**
   * Defines a new Feed.
   * @example
   * Feed.define({ student: 'abigailkealoha',
   *                 opportunity: 'att-hackathon
   *                 course: undefined
   *                 feedType: 'verified'
   *                 timestamp: '12345465465',
   * @param { Object } description Object with keys student, opportunity, course, feedType, and timestamp.
   * @returns The newly created docID.
   */
  define({ student, opportunity, course, feedType, timestamp }) {
    let studentID;
    let opportunityID;
    let courseID;
    let picture;
    let slugID;
    if (student) {
      studentID = Users.getID(student);
    }
    if (opportunity) {
      opportunityID = Opportunities.getID(opportunity);
    }
    if (course) {
      courseID = Courses.getID(course);
    }
    let description = '';
    if (feedType === 'new') {
      if (student !== undefined) {
        description = `${Users.getFullName(studentID)} has joined RadGrad.`;
        slugID = Slugs.define({ name: `feed-${Users.findDoc(studentID).username}-new`, entityName: this.getType() });
        picture = Users.findDoc(studentID).picture;
        if (!picture) {
          picture = '/images/ICS-logo.png';
        }
      } else if (opportunity !== undefined) {
        description = `${Opportunities.findDoc(opportunityID).name} has been added to Opportunities.`;
        slugID = Slugs.define({ name: `feed-${Slugs.getDoc(Opportunities.findDoc(opportunityID).slugID).name}-new`,
          entityName: this.getType() });
        picture = '/images/radgrad_logo.png';
      } else if (course !== undefined) {
        description = `${Courses.findDoc(courseID).name} has been added to Courses.`;
        slugID = Slugs.define({ name: `${Slugs.getDoc(Courses.findDoc(courseID).slugID).name}-new`,
          entityName: this.getType() });
        picture = '/images/radgrad_logo.png';
      }
    } else if (feedType === 'verified') {
      description = `${Users.getFullName(studentID)} has been verified for
        ${Opportunities.findDoc(opportunityID).name} (${Semesters.toString(opportunity.semesterID, false)}).`;
      const username = Users.findDoc(studentID).username;
      const oppDate = Semesters.toString(opportunity.semesterID, true);
      const oppName = Slugs.findDoc((Opportunities.findDoc(opportunityID).slugID)).name;
      slugID = Slugs.define({ name: `feed-${username}-${oppName}-${oppDate}-new`, entityName: this.getType() });
      console.log(`feed-${username}-${oppName}-${oppDate}-new`);
      picture = Users.findDoc(studentID).picture;
    }
    const feedID = this._collection.insert({ slugID, studentID, opportunityID, courseID,
      description, timestamp, picture });
    Slugs.updateEntityID(slugID, feedID);
    return feedID;
  }
  getSlug(feedID) {
    this.assertDefined(feedID);
    const feedDoc = this.findDoc(feedID);
    return Slugs.findDoc(feedDoc.slugID).name;
  }
}

/**
 * Provides the singleton instance of this class to all other entities.
 */
export const Feed = new FeedCollection();

<|endoftext|>


<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
Copyright 2013 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* globals chrome */

'use strict';
(function ExtensionRouterClosure() {
  var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html');
  var CRX_BASE_URL = chrome.extension.getURL('/');

  // TODO(rob): Use declarativeWebRequest once declared URL-encoding is
  //            supported, see http://crbug.com/273589
  //            (or rewrite the query string parser in viewer.js to get it to
  //             recognize the non-URL-encoded PDF URL.)
  chrome.webRequest.onBeforeRequest.addListener(function(details) {
    // This listener converts chrome-extension://.../http://...pdf to
    // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
    var url = details.url.substring(CRX_BASE_URL.length);
    var matchingUrl = /^(?:https?|file|ftp|chrome-extension)(:|%3A)/.exec(url);
    if (matchingUrl) {
      // location.hash is restored when "#" is missing from URL.
      url = url.split('#')[0];
      if (matchingUrl[1] === ':') {
        url = encodeURIComponent(url);
      }
      url = VIEWER_URL + '?file=' + url;
      console.log('Redirecting ' + details.url + ' to ' + url);
      return { redirectUrl: url };
    }
  }, {
      types: ['main_frame', 'sub_frame'],
      urls: [
        CRX_BASE_URL + 'http*', // and https
        CRX_BASE_URL + 'file*',
        CRX_BASE_URL + 'ftp*',
        CRX_BASE_URL + 'chrome-extension*'
      ]
  }, ['blocking']);
  console.log('Set up extension URL router.');
})();
<commit_msg>[CRX] Reload extension tab after session restore<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
Copyright 2013 Mozilla Foundation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* globals chrome */

'use strict';
(function ExtensionRouterClosure() {
  var VIEWER_URL = chrome.extension.getURL('content/web/viewer.html');
  var CRX_BASE_URL = chrome.extension.getURL('/');

  // TODO(rob): Use declarativeWebRequest once declared URL-encoding is
  //            supported, see http://crbug.com/273589
  //            (or rewrite the query string parser in viewer.js to get it to
  //             recognize the non-URL-encoded PDF URL.)
  chrome.webRequest.onBeforeRequest.addListener(function(details) {
    // This listener converts chrome-extension://.../http://...pdf to
    // chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
    var url = details.url.substring(CRX_BASE_URL.length);
    var matchingUrl = /^(?:https?|file|ftp|chrome-extension)(:|%3A)/.exec(url);
    if (matchingUrl) {
      // location.hash is restored when "#" is missing from URL.
      url = url.split('#')[0];
      if (matchingUrl[1] === ':') {
        url = encodeURIComponent(url);
      }
      url = VIEWER_URL + '?file=' + url;
      console.log('Redirecting ' + details.url + ' to ' + url);
      return { redirectUrl: url };
    }
  }, {
      types: ['main_frame', 'sub_frame'],
      urls: [
        CRX_BASE_URL + 'http*', // and https
        CRX_BASE_URL + 'file*',
        CRX_BASE_URL + 'ftp*',
        CRX_BASE_URL + 'chrome-extension*'
      ]
  }, ['blocking']);

  // When session restore is used, viewer pages may be loaded before the
  // webRequest event listener is attached (= page not found).
  // Reload these tabs.
  chrome.tabs.query({
    url: CRX_BASE_URL + '*://*'
  }, function(tabsFromLastSession) {
    for (var i = 0; i < tabsFromLastSession.length; ++i) {
      chrome.tabs.reload(tabsFromLastSession[i].id);
    }
  });
  console.log('Set up extension URL router.');
})();
<|endoftext|>


<commit_before>var React = require('react');
var RoleActions = require('../actions/RoleActions');
var AppActions = require('../actions/AppActions');
var RoleStore = require('../stores/RoleStore');
var AppStore = require('../stores/AppStore');
var UI = require('./UI.react');
var BS = require('react-bootstrap');
var _ = require('lodash');

function getStateFromStores() {
  return {
    roles: RoleStore.get(),
    apps: AppStore.get()
  };
}

var AdminRoles = React.createClass({

  getInitialState: function() {
    return getStateFromStores();
  },

  componentDidMount: function() {
    RoleStore.addChangeListener(this._onChange);
    AppStore.addChangeListener(this._onChange);
    if (this.props.token) {
      AppActions.getApps(this.props.token);
      RoleActions.getRoles(this.props.token);
    }
  },

  componentWillUnmount: function() {
    RoleStore.removeChangeListener(this._onChange);
    AppStore.removeChangeListener(this._onChange);
  },

  saveRole: function(role) {
    RoleActions.save(this.props.token, role);
  },

  deleteRole: function(role) {
    RoleActions.delete(this.props.token, role.id);
  },

  render: function() {
    return (

        <UI.PageHeader title="Administration: Roles">
          <BS.ModalTrigger modal={<RoleModal apps={this.state.apps} onRoleSaved={this.saveRole} />}>
            <BS.Button bsStyle="primary" className="pull-right">
               New Role
            </BS.Button>
          </BS.ModalTrigger>
        </UI.PageHeader>

                Name
                All Apps
                Apps

              {this.state.roles.map(function(role, i) {
                var apps = [];
                if (role.apps) {
                  apps = role.apps;
                }
                var app_names = [];
                apps.map(function(client_id) {
                  var app = _.find(this.state.apps, { client_id: client_id });
                  if (app) {
                    app_names.push(app.name);
                  } else {
                    app_names.push(client_id);
                  }
                }, this);

                return (

                    {role.name}
                    {role.all_apps == true ? 'Yes' : 'No'}

                      {app_names.map(function(app) {
                        return (
{app}
);
                      })}

                      <BS.ModalTrigger modal={<UI.PromptModal message="Are you sure you want to delete this role?" onAcceptDialog={this.deleteRole.bind(this, role)} />}>

                      </BS.ModalTrigger>

                      <BS.ModalTrigger modal={<RoleModal apps={this.state.apps} role={role} onRoleSaved={this.saveRole} />}>

                      </BS.ModalTrigger>

                );
              }, this)}

    );
  },

  /**
   * Event handler for 'change' events coming from the stores
   */
  _onChange: function() {
    this.setState(getStateFromStores());
  }
});

var Forms = require('./Forms.react');

var RoleModal = React.createClass({
  getInitialState: function() {
    var role = this.props.role || {};
    return {
      id: role.id,
      name: role.name,
      all_apps: role.all_apps || false,
      apps: role.apps || []
    };
  },

  render: function() {

    var appList = [];

    if (!this.state.all_apps) {
      {this.props.apps.map((function(app, i) {
        var checked = this.state.apps.indexOf(app.client_id) > -1;
        appList.push(<Forms.Checkbox key={app.client_id} name={app.client_id} label={app.name} checked={checked} onChange={this.onAppSelected} />);
      }).bind(this))}
    }

    return(
      <BS.Modal {...this.props} animation={false} title="New Role">

          <form className="form-horizontal">
            <Forms.TextInput name="name" label="Name" placeholder="Name" value={this.state.name} onChange={this.onNameChanged} />
            <Forms.RadioGroup label="Apps">
              <Forms.Radio label="All Apps" name="all_apps" value="1" checked={this.state.all_apps} onChange={this.allAppsChanged} />
              <Forms.Radio label="Specific Apps" name="all_apps" value="0" checked={!this.state.all_apps} onChange={this.allAppsChanged} />
            </Forms.RadioGroup>
            {appList}
          </form>

          <BS.Button onClick={this.props.onRequestHide}>Close</BS.Button>
          <BS.Button className="btn btn-primary" onClick={this.saveChanges}>Save changes</BS.Button>

      </BS.Modal>
    )
  },

  saveChanges: function() {
    // TODO: Validation logic
    var role = {
      id: this.state.id,
      name: this.state.name,
      all_apps: this.state.all_apps,
      apps: this.state.apps
    };
    this.props.onRoleSaved(role);
    this.props.onRequestHide();
  },

  allAppsChanged: function(event) {
    this.setState({all_apps: event.target.value === "1" });
  },

  onNameChanged: function(event) {
    this.setState({name: event.target.value });
  },

  onAppSelected: function(event) {
    var apps = this.state.apps;
    var client_id = event.target.name;
    var i = apps.indexOf(client_id);
    if (i > -1) {
      apps.splice(i, 1);
    } else {
      apps.push(client_id);
    }
    this.setState({ apps: apps });
  }

});

module.exports = AdminRoles;
<commit_msg>Fixed issue where all apps and specific apps can be set. Closes #19<commit_after>var React = require('react');
var RoleActions = require('../actions/RoleActions');
var AppActions = require('../actions/AppActions');
var RoleStore = require('../stores/RoleStore');
var AppStore = require('../stores/AppStore');
var UI = require('./UI.react');
var BS = require('react-bootstrap');
var _ = require('lodash');

function getStateFromStores() {
  return {
    roles: RoleStore.get(),
    apps: AppStore.get()
  };
}

var AdminRoles = React.createClass({

  getInitialState: function() {
    return getStateFromStores();
  },

  componentDidMount: function() {
    RoleStore.addChangeListener(this._onChange);
    AppStore.addChangeListener(this._onChange);
    if (this.props.token) {
      AppActions.getApps(this.props.token);
      RoleActions.getRoles(this.props.token);
    }
  },

  componentWillUnmount: function() {
    RoleStore.removeChangeListener(this._onChange);
    AppStore.removeChangeListener(this._onChange);
  },

  saveRole: function(role) {
    RoleActions.save(this.props.token, role);
  },

  deleteRole: function(role) {
    RoleActions.delete(this.props.token, role.id);
  },

  render: function() {
    return (

        <UI.PageHeader title="Administration: Roles">
          <BS.ModalTrigger modal={<RoleModal apps={this.state.apps} onRoleSaved={this.saveRole} />}>
            <BS.Button bsStyle="primary" className="pull-right">
               New Role
            </BS.Button>
          </BS.ModalTrigger>
        </UI.PageHeader>

                Name
                All Apps
                Apps

              {this.state.roles.map(function(role, i) {
                var apps = [];
                if (role.apps) {
                  apps = role.apps;
                }
                var app_names = [];
                apps.map(function(client_id) {
                  var app = _.find(this.state.apps, { client_id: client_id });
                  if (app) {
                    app_names.push(app.name);
                  } else {
                    app_names.push(client_id);
                  }
                }, this);

                return (

                    {role.name}
                    {role.all_apps == true ? 'Yes' : 'No'}

                      {app_names.map(function(app) {
                        return (
{app}
);
                      })}

                      <BS.ModalTrigger modal={<UI.PromptModal message="Are you sure you want to delete this role?" onAcceptDialog={this.deleteRole.bind(this, role)} />}>

                      </BS.ModalTrigger>

                      <BS.ModalTrigger modal={<RoleModal apps={this.state.apps} role={role} onRoleSaved={this.saveRole} />}>

                      </BS.ModalTrigger>

                );
              }, this)}

    );
  },

  /**
   * Event handler for 'change' events coming from the stores
   */
  _onChange: function() {
    this.setState(getStateFromStores());
  }
});

var Forms = require('./Forms.react');

var RoleModal = React.createClass({
  getInitialState: function() {
    var role = this.props.role || {};
    return {
      id: role.id,
      name: role.name,
      all_apps: role.all_apps || false,
      apps: role.apps || []
    };
  },

  render: function() {

    var appList = [];

    if (!this.state.all_apps) {
      {this.props.apps.map((function(app, i) {
        var checked = this.state.apps.indexOf(app.client_id) > -1;
        appList.push(<Forms.Checkbox key={app.client_id} name={app.client_id} label={app.name} checked={checked} onChange={this.onAppSelected} />);
      }).bind(this))}
    }

    return(
      <BS.Modal {...this.props} animation={false} title="New Role">

          <form className="form-horizontal">
            <Forms.TextInput name="name" label="Name" placeholder="Name" value={this.state.name} onChange={this.onNameChanged} />
            <Forms.RadioGroup label="Apps">
              <Forms.Radio label="All Apps" name="all_apps" value="1" checked={this.state.all_apps} onChange={this.allAppsChanged} />
              <Forms.Radio label="Specific Apps" name="all_apps" value="0" checked={!this.state.all_apps} onChange={this.allAppsChanged} />
            </Forms.RadioGroup>
            {appList}
          </form>

          <BS.Button onClick={this.props.onRequestHide}>Close</BS.Button>
          <BS.Button className="btn btn-primary" onClick={this.saveChanges}>Save changes</BS.Button>

      </BS.Modal>
    )
  },

  saveChanges: function() {
    // TODO: Validation logic
    var role = {
      id: this.state.id,
      name: this.state.name,
      all_apps: this.state.all_apps,
      apps: this.state.apps
    };
    this.props.onRoleSaved(role);
    this.props.onRequestHide();
  },

  allAppsChanged: function(event) {
    var all_apps = event.target.value === "1";
    this.setState({all_apps: all_apps });
    if (all_apps) {
      this.setState({ apps: [] }) ;
    }
  },

  onNameChanged: function(event) {
    this.setState({name: event.target.value });
  },

  onAppSelected: function(event) {
    var apps = this.state.apps;
    var client_id = event.target.name;
    var i = apps.indexOf(client_id);
    if (i > -1) {
      apps.splice(i, 1);
    } else {
      apps.push(client_id);
    }
    this.setState({ apps: apps });
  }

});

module.exports = AdminRoles;
<|endoftext|>


<commit_before>/*!
{
  "name": "Local Storage",
  "property": "localstorage",
  "caniuse": "namevalue-storage",
  "tags": ["storage"],
  "knownBugs": [],
  "notes": [],
  "warnings": [],
  "polyfills": [
    "joshuabell-polyfill",
    "cupcake",
    "storagepolyfill",
    "amplifyjs",
    "yui-cacheoffline"
  ]
}
!*/
define(['Modernizr'], function(Modernizr) {
  // In FF4, if disabled, window.localStorage should === null.

  // Normally, we could not test that directly and need to do a
  //   `('localStorage' in window) && ` test first because otherwise Firefox will
  //   throw bugzil.la/365772 if cookies are disabled

  // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
  // will throw the exception:
  //   QUOTA_EXCEEDED_ERROR DOM Exception 22.
  // Peculiarly, getItem and removeItem calls do not throw.

  // Because we are forced to try/catch this, we'll go aggressive.

  // Just FWIW: IE8 Compat mode supports these features completely:
  //   www.quirksmode.org/dom/html5.html
  // But IE8 doesn't support either with local files

  Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
      localStorage.setItem(mod, mod);
      localStorage.removeItem(mod);
      return true;
    } catch (e) {
      return false;
    }
  });
});
<commit_msg>localstorage: compat notes (#1993)<commit_after>/*!
{
  "name": "Local Storage",
  "property": "localstorage",
  "caniuse": "namevalue-storage",
  "tags": ["storage"],
  "knownBugs": [],
  "notes": [],
  "warnings": [],
  "polyfills": [
    "joshuabell-polyfill",
    "cupcake",
    "storagepolyfill",
    "amplifyjs",
    "yui-cacheoffline"
  ]
}
!*/
define(['Modernizr'], function(Modernizr) {
  // In FF4, if disabled, window.localStorage should === null.

  // Normally, we could not test that directly and need to do a
  //   `('localStorage' in window)` test first because otherwise Firefox will
  //   throw bugzil.la/365772 if cookies are disabled

  // Similarly, in Chrome with "Block third-party cookies and site data" enabled,
  // attempting to access `window.sessionStorage` will throw an exception. crbug.com/357625

  // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
  // will throw the exception:
  //   QUOTA_EXCEEDED_ERROR DOM Exception 22.
  // Peculiarly, getItem and removeItem calls do not throw.

  // Because we are forced to try/catch this, we'll go aggressive.

  // Just FWIW: IE8 Compat mode supports these features completely:
  //   www.quirksmode.org/dom/html5.html
  // But IE8 doesn't support either with local files

  Modernizr.addTest('localstorage', function() {
    var mod = 'modernizr';
    try {
      localStorage.setItem(mod, mod);
      localStorage.removeItem(mod);
      return true;
    } catch (e) {
      return false;
    }
  });
});
<|endoftext|>


<commit_before>App.module("Predictor", function(Mod, App, Backbone, Marionette, $, _) {

	Mod.views.MapView = Marionette.ItemView.extend({
		template: 'mapView',
		tagName: 'div',
		className: 'map-wrapper',
		model: Mod.FlightModel,
		ui: {
			map: '#map'
		},

		initialize: function() {
			// Bind events for this
			_.bindAll(this, 'resize', 'centerMap');
			// Bind resize event to manage map size
			$(window).on('resize', this.resize);
		},

		onClick: function(e) {
			App.vent.trigger('Map:Click', e);
		},

		centerMap: function(lat, lon) {
			this.map.panTo(new L.LatLng(lat, lon));
		},

		onShow: function() {
			// TODO Map attribution
			var mapLayer = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg',{
				attribution: 'Map data © OpenStreetMap, Tiles Courtesy of MapQuest ',
				maxZoom: 18,
				subdomains: '1234'
			});
			var satLayer = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',{
				attribution: '
Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency',
				maxZoom: 18,
				subdomains: '1234'
			});
			var hybLayer = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/hyb/{z}/{x}/{y}.jpg',{
				attribution: 'Map data © OpenStreetMap, Tiles Courtesy of MapQuest ',
				maxZoom: 18,
				subdomains: '1234'
			});
			var hybGroup = L.layerGroup([satLayer, hybLayer]);
			var baseMaps = {
				"Map": mapLayer,
				"Hybrid": hybGroup
			};
			this.resize();
			this.map = L.map('map', {
				layers: [mapLayer],
				center: new L.LatLng(42, -94),
				zoom: 10
			});
			L.control.layers(baseMaps).addTo(this.map);

			// Add scale
			L.control.scale().addTo(this.map);

			// Bind events
			this.map.on('click', this.onClick);
			App.vent.on('Map:Center', this.centerMap);
		},

		resize: function() {
			// TODO bottom padding
			this.ui.map.height($(window).height() - this.ui.map.offset().top - 10);
		},

		onClose: function() {
			// Unbind events
			$(window).off('resize', this.resize);
			this.map.off('click', this.onClick);
			App.vent.off('Map:Center', this.centerMap);
		},

		addJson: function(data) {
			var json = L.geoJson(data);
			this.map.fitBounds(json.getBounds());
			json.addTo(this.map);
		}
	});

});<commit_msg>Adding logic so that we get basic Open Street map tiles. (#61)<commit_after>App.module("Predictor", function(Mod, App, Backbone, Marionette, $, _) {

	Mod.views.MapView = Marionette.ItemView.extend({
		template: 'mapView',
		tagName: 'div',
		className: 'map-wrapper',
		model: Mod.FlightModel,
		ui: {
			map: '#map'
		},

		initialize: function() {
			// Bind events for this
			_.bindAll(this, 'resize', 'centerMap');
			// Bind resize event to manage map size
			$(window).on('resize', this.resize);
		},

		onClick: function(e) {
			App.vent.trigger('Map:Click', e);
		},

		centerMap: function(lat, lon) {
			this.map.panTo(new L.LatLng(lat, lon));
		},

		onShow: function() {
			// TODO Map attribution
            var mapLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
                attribution: 'Map data © OpenStreetMap, Tiles Courtesy of MapQuest ',
                maxZoom: 18,
                subdomains: ['a','b','c']
            });
            var satLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
                attribution: 'Map data © OpenStreetMap, Tiles Courtesy of MapQuest ',
                maxZoom: 18,
                subdomains: ['a','b','c']
            });
            var hybLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
                attribution: 'Map data © OpenStreetMap, Tiles Courtesy of MapQuest ',
                maxZoom: 18,
                subdomains: ['a','b','c']
            });
			var hybGroup = L.layerGroup([satLayer, hybLayer]);
			var baseMaps = {
				"Map": mapLayer,
				"Hybrid": hybGroup
			};
			this.resize();
			this.map = L.map('map', {
				layers: [mapLayer],
				center: new L.LatLng(42, -94),
				zoom: 10
			});
			L.control.layers(baseMaps).addTo(this.map);

			// Add scale
			L.control.scale().addTo(this.map);

			// Bind events
			this.map.on('click', this.onClick);
			App.vent.on('Map:Center', this.centerMap);
		},

		resize: function() {
			// TODO bottom padding
			this.ui.map.height($(window).height() - this.ui.map.offset().top - 10);
		},

		onClose: function() {
			// Unbind events
			$(window).off('resize', this.resize);
			this.map.off('click', this.onClick);
			App.vent.off('Map:Center', this.centerMap);
		},

		addJson: function(data) {
			var json = L.geoJson(data);
			this.map.fitBounds(json.getBounds());
			json.addTo(this.map);
		}
	});

});<|endoftext|>


<commit_before>class AdminController {
    constructor(data) {
        this.data = data;
    }

    getViewBookings(req, res) {
        Promise.resolve(this.data.users.getAll())
            .then((users) => {
                return res.render('auth/admin/viewbookings', {
                    context: users,
                    req: req,
                    moment: require('moment'),
                });
            });
    }

    getViewCars(req, res) {
        Promise.resolve(this.data.cars.getAll())
            .then((cars) => {
                return res.render('auth/admin/viewcars', {
                    context: cars,
                    req: req,
                });
            });
    }

    getViewUsers(req, res) {
        Promise.resolve(this.data.users.getAll())
            .then((user) => {
                return res.render('auth/admin/viewusers', {
                    context: user,
                    req: req,
                });
            });
    }

    getViewDeals(req, res) {
        Promise.resolve(this.data.cars.filterBy({ 'specialpriceactivated': '1' }))
            .then((deals) => {
                return res.render('auth/admin/viewdeals', {
                    context: deals,
                    req: req,
                });
            });
    }

    getEditUserByIdForm(req, res) {
        Promise.resolve(this.data.users.findById(req.params.id))
            .then((user) => {
                return res.render('auth/admin/edituser', {
                    user,
                    req: req,
                });
            });
    }

    updateUserProfile(req, res) {
        const bodyUser = req.body;
        return this._updateUserProperties(req.params.id, bodyUser)
            .then((user) => {
                return this.data.users.updateById(user);
            })
            .then(() => {
                return res.status(200).redirect('/auth/viewusers');
            })
            .catch((err) => {
                req.toastr.error(err.message);
                return res.status(400).redirect('/auth/viewusers');
            });
    }

    _updateUserProperties(userId, user) {
        return this.data.users.findById(userId)
            .then((dbUser) => {
                user._id = dbUser._id;
                user.username = dbUser.username;
                user.role = dbUser.role;
                user.bookings = dbUser.bookings;
                user.passHash = dbUser.passHash;

                return Promise.resolve(user);
            });
    }
}

const init = (data) => {
    return new AdminController(data);
};

module.exports = { init };<commit_msg>added returns to methods<commit_after>class AdminController {
    constructor(data) {
        this.data = data;
    }

    getViewBookings(req, res) {
        return Promise.resolve(this.data.users.getAll())
            .then((users) => {
                return res.render('auth/admin/viewbookings', {
                    context: users,
                    req: req,
                    moment: require('moment'),
                });
            });
    }

    getViewCars(req, res) {
        return Promise.resolve(this.data.cars.getAll())
            .then((cars) => {
                return res.render('auth/admin/viewcars', {
                    context: cars,
                    req: req,
                });
            });
    }

    getViewUsers(req, res) {
        return Promise.resolve(this.data.users.getAll())
            .then((user) => {
                return res.render('auth/admin/viewusers', {
                    context: user,
                    req: req,
                });
            });
    }

    getViewDeals(req, res) {
        return Promise.resolve(this.data.cars.filterBy({ 'specialpriceactivated': '1' }))
            .then((deals) => {
                return res.render('auth/admin/viewdeals', {
                    context: deals,
                    req: req,
                });
            });
    }

    getEditUserByIdForm(req, res) {
        return Promise.resolve(this.data.users.findById(req.params.id))
            .then((user) => {
                return res.render('auth/admin/edituser', {
                    user,
                    req: req,
                });
            });
    }

    updateUserProfile(req, res) {
        const bodyUser = req.body;
        return this._updateUserProperties(req.params.id, bodyUser)
            .then((user) => {
                return this.data.users.updateById(user);
            })
            .then(() => {
                return res.status(200).redirect('/auth/viewusers');
            })
            .catch((err) => {
                req.toastr.error(err.message);
                return res.status(400).redirect('/auth/viewusers');
            });
    }

    _updateUserProperties(userId, user) {
        return this.data.users.findById(userId)
            .then((dbUser) => {
                user._id = dbUser._id;
                user.username = dbUser.username;
                user.role = dbUser.role;
                user.bookings = dbUser.bookings;
                user.passHash = dbUser.passHash;

                return Promise.resolve(user);
            });
    }
}

const init = (data) => {
    return new AdminController(data);
};

module.exports = { init };
<|endoftext|>


<commit_before>'use strict';

module.exports = function(app) {
app.route('/categories')
  .get(function (request, response) {
    response.json([
      { name: 'Beverages' },
      { name: 'Condiments' }
    ]);
  });
};
<commit_msg>Modify route for Categories GET command.<commit_after>'use strict';

module.exports = function(app) {

  var categories = require('../../app/controllers/categories.server.controller');

  // route for GET categories
  app.route('/categories')
    .get(categories.list);
};
<|endoftext|>


<commit_before>function Reporter() {
	this.suites = [];
	this.suppressDateAndTime = process.argv.indexOf("-no-date-time") >= 0;
};

Reporter.prototype.isFirstEncounter = function(suite) {
	for (var i = 0; i < this.suites.length; i++) {
		if (this.suites[i] === suite) {
			return false;
		}
	}

	return true;
};

Reporter.prototype.reportRunnerStarting = function(runner) {
	var showPassed, showSkipped;

	console.log("Running tests...\r\n");

	this.startedAt = new Date();
};

Reporter.prototype.reportRunnerResults = function(runner) {
	var results = runner.results();
	var specs = runner.specs();
	var message = "\r\n" + specs.length + " spec" + (specs.length === 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount === 1) ? "" : "s");

	if (this.suppressDateAndTime !== true)
		message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";

	console.info(message);

	if (this.suppressDateAndTime !== true)
		console.info("Finished at " + new Date().toString() + "\r\n");

	if (results.failedCount > 0) {
		process.exit(1);
	}
};

Reporter.prototype.reportSuiteResults = function(suite) {
	var results = suite.results();
	var status = results.totalCount > 0 ?
		(results.passed() ? 'passed' : 'failed') : "skipped or empty";

	if (status === "skipped or empty" && this.isFirstEncounter(suite)) {
		console.log("\r\n" + suite.description);
		console.log("=============================================");
	}
	else if (!suite.parentSuite) {
		console.log("---------------------------------------------");
	}

	if (!suite.parentSuite) {
		console.log("suite " + status);
		console.log("=============================================\r\n");
	}
};

Reporter.prototype.reportSpecStarting = function(spec) {
	if (this.isFirstEncounter(spec.suite) && (!spec.suite.parentSuite || this.isFirstEncounter(spec.suite.parentSuite))) {
		console.log("\r\nSuite: \"" + (spec.suite.parentSuite ? spec.suite.parentSuite.description : spec.suite.description) + "\"");
		console.log("=============================================");
	}
	else {
		console.log("---------------------------------------------");
	}

	if (spec.suite.parentSuite) {
		this.suites.push(spec.suite.parentSuite);
	}
	this.suites.push(spec.suite);
	console.log("Running \"" + spec.suite.description + " " + spec.description + "\"...");
};

Reporter.prototype.reportSpecResults = function(spec) {
	var results = spec.results();
	var status = results.passed() ? 'passed' : 'failed';
	if (results.skipped) {
		status = 'skipped';
	}

	var resultItems = results.getItems();
	for (var i = 0; i < resultItems.length; i++) {
		var result = resultItems[i];

		if (result.type == 'log') {
			console.log("log:  " + result.toString());
		}
		else if (result.type == 'expect' && result.passed && !result.passed()) {
			if (result.passed()) {
				console.log("passed: " + result.message);
			}
			else {
				console.error("FAILED:  " + result.message);

				if (result.trace.stack) {
					console.log(result.trace.stack);
				}
			}
		}
	}
};

exports.Reporter = Reporter;

<commit_msg>include full suite name in output<commit_after>function Reporter() {
	this.suites = [];
	this.suppressDateAndTime = process.argv.indexOf("-no-date-time") >= 0;
};

Reporter.prototype.isFirstEncounter = function(suite) {
	for (var i = 0; i < this.suites.length; i++) {
		if (this.suites[i] === suite) {
			return false;
		}
	}

	return true;
};

Reporter.prototype.reportRunnerStarting = function(runner) {
	var showPassed, showSkipped;

	console.log("Running tests...\r\n");

	this.startedAt = new Date();
};

Reporter.prototype.reportRunnerResults = function(runner) {
	var results = runner.results();
	var specs = runner.specs();
	var message = "\r\n" + specs.length + " spec" + (specs.length === 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount === 1) ? "" : "s");

	if (this.suppressDateAndTime !== true)
		message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";

	console.info(message);

	if (this.suppressDateAndTime !== true)
		console.info("Finished at " + new Date().toString() + "\r\n");

	if (results.failedCount > 0) {
		process.exit(1);
	}
};

Reporter.prototype.reportSuiteResults = function(suite) {
	var results = suite.results();
	var status = results.totalCount > 0 ?
		(results.passed() ? 'passed' : 'failed') : "skipped or empty";

	if (status === "skipped or empty" && this.isFirstEncounter(suite)) {
		console.log("\r\n" + suite.description);
		console.log("=============================================");
	}
	else if (!suite.parentSuite) {
		console.log("---------------------------------------------");
	}

	if (!suite.parentSuite) {
		console.log("suite " + status);
		console.log("=============================================\r\n");
	}
};

Reporter.prototype.reportSpecStarting = function(spec) {
	if (this.isFirstEncounter(spec.suite) && (!spec.suite.parentSuite || this.isFirstEncounter(spec.suite.parentSuite))) {
		console.log("\r\nSuite: \"" + (spec.suite.parentSuite ? spec.suite.parentSuite.description : spec.suite.description) + "\"");
		console.log("=============================================");
	}
	else {
		console.log("---------------------------------------------");
	}

	if (spec.suite.parentSuite) {
		this.suites.push(spec.suite.parentSuite);
	}
	this.suites.push(spec.suite);

	var suiteDescription = spec.suite.description;
	var parentSuite = spec.suite.parentSuite;
	while (parentSuite) {
		suiteDescription = parentSuite.description + " " + suiteDescription;
		parentSuite = parentSuite.parentSuite;
	}
	console.log("Running \"" + suiteDescription + " " + spec.description + "\"...");
};

Reporter.prototype.reportSpecResults = function(spec) {
	var results = spec.results();
	var status = results.passed() ? 'passed' : 'failed';
	if (results.skipped) {
		status = 'skipped';
	}

	var resultItems = results.getItems();
	for (var i = 0; i < resultItems.length; i++) {
		var result = resultItems[i];

		if (result.type == 'log') {
			console.log("log:  " + result.toString());
		}
		else if (result.type == 'expect' && result.passed && !result.passed()) {
			if (result.passed()) {
				console.log("passed: " + result.message);
			}
			else {
				console.error("FAILED:  " + result.message);

				if (result.trace.stack) {
					console.log(result.trace.stack);
				}
			}
		}
	}
};

exports.Reporter = Reporter;

<|endoftext|>


<commit_before>window.Rendxx = window.Rendxx || {};
window.Rendxx.Game = window.Rendxx.Game || {};
window.Rendxx.Game.Ghost = window.Rendxx.Game.Ghost || {};
window.Rendxx.Game.Ghost.System = window.Rendxx.Game.Ghost.System || {};

/**
 * Core entry of Game.Ghost system
 */
(function (SYSTEM) {
    var Data = SYSTEM.Data;
    /**
     * Game Entity
     */
    var Core = function () {
        // data ---------------------------------------------------
        var that = this,
            isStarted = false,
            gameData = {},      // store all data in the game, use to render
            intervalFunc = null,
            flag_started = false,
            flag_setuped = false;

        // component ----------------------------------------------
        this.map = null;
        this.sound = null;
        this.message = null;
        this.userInput = null;
        this.interAction = null;
        this.characterManager = null;

        // message -----------------------------------------------
        this.send = null;

        this.receive = function (msg) {
        };

        this.action = function (clientId, dat) {
            if (!isStarted) return;
            this.userInput.action(clientId, dat);
        };

        // callback -----------------------------------------------
        this.onUpdated = null;       // callback for render
        this.onSetuped = null;
        this.clientSetup = null;    // (target, clientData)
        this.clientUpdate = null;   // (target, clientData)
        this.onEnd = null;          // (isWin)

        // public method ------------------------------------------
        // reset game with given data
        this.reset = function (setupData_in, gameData_in) {
            if (setupData_in === undefined || setupData_in === null) return;
            gameData = gameData_in;
            initComponent(setupData_in.model, setupData_in.map, setupData_in.player);
            this.characterManager.reset(setupData_in.player, (gameData_in !== null && gameData_in !== undefined) ? gameData_in.characters : null);
            this.map.reset(setupData_in.mapSetup);
            this.interAction.reset();
            this.userInput.reset(this.characterManager.characters, this.characterManager.index2Id);
            flag_setuped = true;
            if (flag_started && !isStarted) this.start();
        };

        // setup game
        this.setup = function (modelData, mapData, playerData) {
            gameData.map = {};
            gameData.characters = [];
            initComponent(modelData, mapData, playerData);
            this.map.setup();
            this.interAction.reset();
            this.characterManager.setup(this.map.position.survivor, this.map.position.ghost);
            this.userInput.reset(this.characterManager.characters, this.characterManager.index2Id);
            var setupData = {
                'model': modelData,
                'map': mapData,
                'mapSetup': this.map.setupData,
                'player': this.characterManager.setupData
            };
            for (var i in playerData) {
                var c =
                this.clientSetup([i], {
                    role: playerData[i].role,
                    portrait: this.characterManager.characters[this.characterManager.id2Index[i]].modelData.portrait,
                    color: this.characterManager.characters[this.characterManager.id2Index[i]].color
                });
            }

            flag_setuped = true;
            this.onSetuped(setupData);
            if (flag_started && !isStarted) this.start();
        };

        // game ------------------------------------------------
        // start game
        this.start = function () {
            flag_started = true;
            if (flag_setuped === false) return;
            isStarted = true;
            if (intervalFunc !== null) clearInterval(intervalFunc);
            intervalFunc = setInterval(function () { nextInterval(); }, 50);
        };

        // end game
        this.end = function () {
            isStarted = false;
            if (intervalFunc !== null) clearInterval(intervalFunc);

            gameData['end'] = this.characterManager.getEndInfo();
            if (this.onEnd) this.onEnd(gameData['end']);
        };

        // renew game
        this.renew = function () {
            // to do
            isStarted = false;
            if (intervalFunc !== null) clearInterval(intervalFunc);

            intervalFunc = null;
        };

        // pause game
        this.pause = function () { };

        // continue game
        this.continue = function () { };

        // private method -----------------------------------------
        // called every time frame
        var nextInterval = function () {
            try{
                that.interAction.update();
                that.characterManager.update();
                gameData.message = that.message.getNewMsg();
                gameData.sound = that.sound.getSoundDat();
                if (that.characterManager.checkEnd()) { that.end(); }
                if (that.onUpdated) that.onUpdated(gameData);
            } catch (e) {
                console.log(e);
            }
        };

        // init component
        var initComponent = function (modelData, mapData, playerData) {
            that.map = new SYSTEM.Map(that, modelData, mapData, gameData.map);
            that.characterManager = new SYSTEM.CharacterManager(that, modelData, playerData, gameData.characters);
            that.interAction = new SYSTEM.InterAction(that);
            that.message = new SYSTEM.Message();
            that.sound = new SYSTEM.Sound();
            that.userInput = new SYSTEM.UserInput(that);
        };
    };

    SYSTEM.Core = Core;
})(window.Rendxx.Game.Ghost.System);<commit_msg>spped up to about 30 pfs<commit_after>window.Rendxx = window.Rendxx || {};
window.Rendxx.Game = window.Rendxx.Game || {};
window.Rendxx.Game.Ghost = window.Rendxx.Game.Ghost || {};
window.Rendxx.Game.Ghost.System = window.Rendxx.Game.Ghost.System || {};

/**
 * Core entry of Game.Ghost system
 */
(function (SYSTEM) {
    var Data = SYSTEM.Data;
    /**
     * Game Entity
     */
    var Core = function () {
        // data ---------------------------------------------------
        var that = this,
            isStarted = false,
            gameData = {},      // store all data in the game, use to render
            intervalFunc = null,
            flag_started = false,
            flag_setuped = false;

        // component ----------------------------------------------
        this.map = null;
        this.sound = null;
        this.message = null;
        this.userInput = null;
        this.interAction = null;
        this.characterManager = null;

        // message -----------------------------------------------
        this.send = null;

        this.receive = function (msg) {
        };

        this.action = function (clientId, dat) {
            if (!isStarted) return;
            this.userInput.action(clientId, dat);
        };

        // callback -----------------------------------------------
        this.onUpdated = null;       // callback for render
        this.onSetuped = null;
        this.clientSetup = null;    // (target, clientData)
        this.clientUpdate = null;   // (target, clientData)
        this.onEnd = null;          // (isWin)

        // public method ------------------------------------------
        // reset game with given data
        this.reset = function (setupData_in, gameData_in) {
            if (setupData_in === undefined || setupData_in === null) return;
            gameData = gameData_in;
            initComponent(setupData_in.model, setupData_in.map, setupData_in.player);
            this.characterManager.reset(setupData_in.player, (gameData_in !== null && gameData_in !== undefined) ? gameData_in.characters : null);
            this.map.reset(setupData_in.mapSetup);
            this.interAction.reset();
            this.userInput.reset(this.characterManager.characters, this.characterManager.index2Id);
            flag_setuped = true;
            if (flag_started && !isStarted) this.start();
        };

        // setup game
        this.setup = function (modelData, mapData, playerData) {
            gameData.map = {};
            gameData.characters = [];
            initComponent(modelData, mapData, playerData);
            this.map.setup();
            this.interAction.reset();
            this.characterManager.setup(this.map.position.survivor, this.map.position.ghost);
            this.userInput.reset(this.characterManager.characters, this.characterManager.index2Id);
            var setupData = {
                'model': modelData,
                'map': mapData,
                'mapSetup': this.map.setupData,
                'player': this.characterManager.setupData
            };
            for (var i in playerData) {
                var c =
                this.clientSetup([i], {
                    role: playerData[i].role,
                    portrait: this.characterManager.characters[this.characterManager.id2Index[i]].modelData.portrait,
                    color: this.characterManager.characters[this.characterManager.id2Index[i]].color
                });
            }

            flag_setuped = true;
            this.onSetuped(setupData);
            if (flag_started && !isStarted) this.start();
        };

        // game ------------------------------------------------
        // start game
        this.start = function () {
            flag_started = true;
            if (flag_setuped === false) return;
            isStarted = true;
            if (intervalFunc !== null) clearInterval(intervalFunc);
            intervalFunc = setInterval(function () { nextInterval(); }, 40);
        };

        // end game
        this.end = function () {
            isStarted = false;
            if (intervalFunc !== null) clearInterval(intervalFunc);

            gameData['end'] = this.characterManager.getEndInfo();
            if (this.onEnd) this.onEnd(gameData['end']);
        };

        // renew game
        this.renew = function () {
            // to do
            isStarted = false;
            if (intervalFunc !== null) clearInterval(intervalFunc);

            intervalFunc = null;
        };

        // pause game
        this.pause = function () { };

        // continue game
        this.continue = function () { };

        // private method -----------------------------------------
        // called every time frame
        var nextInterval = function () {
            try{
                that.interAction.update();
                that.characterManager.update();
                gameData.message = that.message.getNewMsg();
                gameData.sound = that.sound.getSoundDat();
                if (that.characterManager.checkEnd()) { that.end(); }
                if (that.onUpdated) that.onUpdated(gameData);
            } catch (e) {
                console.log(e);
            }
        };

        // init component
        var initComponent = function (modelData, mapData, playerData) {
            that.map = new SYSTEM.Map(that, modelData, mapData, gameData.map);
            that.characterManager = new SYSTEM.CharacterManager(that, modelData, playerData, gameData.characters);
            that.interAction = new SYSTEM.InterAction(that);
            that.message = new SYSTEM.Message();
            that.sound = new SYSTEM.Sound();
            that.userInput = new SYSTEM.UserInput(that);
        };
    };

    SYSTEM.Core = Core;
})(window.Rendxx.Game.Ghost.System);<|endoftext|>