<commit_before>(function(config, models, views, routers, utils, templates) {

views.Post = Backbone.View.extend({

  id: 'post',

  events: {
    'click .save': '_save',
    'click .cancel-save': '_toggleCommit',
    'click .save.confirm': 'updateFile',
    'click a.toggle.view': '_toggleView',
    'click a.toggle.meta': '_toggleMeta',
    'change input': '_makeDirty',
    'change #post_published': 'updateMetaData',
    'click .delete': '_delete',
    'click .toggle-options': '_toggleOptions'
  },

  _toggleOptions: function() {
    $('.options').toggle();
    return false;
  },

  _delete: function() {
    if (confirm("Are you sure you want to delete that file?")) {
      deletePost(app.state.user, app.state.repo, app.state.branch, this.model.path, this.model.file, _.bind(function(err) {
        if (err) return alert('Error during deletion. Please wait 30 seconds and try again.');
        router.navigate([app.state.user, app.state.repo, "tree", app.state.branch].join('/'), true);
      }, this));
    }
    return false;
  },

  updateURL: function() {
    var url = _.compact([app.state.user, app.state.repo, this.model.preview ? "blob" : "edit", app.state.branch, this.model.path, this.model.file]);
    router.navigate(url.join('/'), false);
  },

  _makeDirty: function(e) {
    this.dirty = true;
    if (this.editor) this.model.content = this.editor.getValue();
    if (this.metadataEditor) this.model.raw_metadata = this.metadataEditor.getValue();
    if (!this.$('.button.save').hasClass('saving')) {
      this.$('.button.save').html(this.model.writeable ? "SAVE" : "SUBMIT CHANGE");
      this.$('.button.save').removeClass('inactive error');
    }
  },

  showDiff: function() {
    var text1 = this.model.persisted ? this.prevContent : '';
    var text2 = this.serialize();
    var d = this.dmp.diff_main(text1, text2);
    this.dmp.diff_cleanupSemantic(d);
    var diff = this.dmp.diff_prettyHtml(d).replace(/¶/g, "");
    $('.diff-wrapper .diff').html(diff);
  },

  _toggleCommit: function() {
    if (!this.$('.document-menu').hasClass('commit')) {
      this.$('.commit-message').attr( 'placeholder', "Updated "+$('input.filepath').val());
    }

    this.hideMeta();
    this.$('.button.save').html(this.$('.document-menu').hasClass('commit') ? (this.model.writeable ? "SAVE" : "SUBMIT CHANGE") : "COMMIT");
    this.$('.button.save').toggleClass('confirm');
    this.$('.document-menu').toggleClass('commit');
    this.$('.button.cancel-save').toggle();
    this.$('.document-menu-content .options').hide();
    this.showDiff();
    this.$('.surface').toggle();
    this.$('.diff-wrapper').toggle();
    this.$('.commit-message').focus();

    return false;
  },

  _save: function(e) {
    if (!this.dirty) return false;
    this._toggleCommit();
    e.preventDefault();
    return false;
  },

  _toggleView: function(e) {
    var that = this;
    this.toggleView($(e.currentTarget).attr('data-view'));
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  _toggleMeta: function(e) {
    var that = this;
    if (e) e.preventDefault();
    $('.toggle.meta').toggleClass('active');
    $('.metadata').toggle();
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  refreshCodeMirror: function() {
    if ($('.toggle.meta').hasClass('active')) {
      $('.CodeMirror-scroll').height($('.document').height() / 2);
    } else {
      $('.CodeMirror-scroll').height($('.document').height());
    }
    this.editor.refresh();
    if (this.metadataEditor) this.metadataEditor.refresh();
  },

  toggleView: function(view) {
    this.view = view;
    if (view === 'preview') {
      this.model.preview = true;
      this.$('.post-content').html(marked(this.model.content));
    } else {
      this.model.preview = false;
    }
    this.hideMeta();
    this.updateURL();
    $('.toggle').removeClass('active');
    $('.toggle.'+view).addClass('active');

    $('.document .surface').removeClass('preview cheatsheet compose');
    $('.document .surface').addClass(view);
  },

  hideMeta: function() {
    $('.toggle.meta').removeClass('active');
    $('.metadata').hide();
  },

  right: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'preview') return;
    if (view === 'compose') return this.toggleView('preview');
    return this.toggleView('compose');
  },

  left: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'cheatsheet') return;
    if (view === 'compose') return this.toggleView('cheatsheet');
    return this.toggleView('compose');
  },

  initialize: function() {
    this.dmp = new diff_match_patch();
    this.mode = "edit";
    this.prevContent = this.serialize();
    if (!window.shortcutsRegistered) {
      key('⌘+s, ctrl+s', _.bind(function() { this.updateFile(); return false; }, this));
      key('ctrl+r', _.bind(function() { this.stashApply(); return false; }, this));
      key('ctrl+shift+right', _.bind(function() { this.right(); return false; }, this));
      key('ctrl+shift+left', _.bind(function() { this.left(); return false; }, this));
      key('esc', _.bind(function() { this.toggleView('compose'); return false; }, this));
      window.shortcutsRegistered = true;
    }

    // Stash editor and metadataEditor content to localStorage on pagehide event
    window.addEventListener('pagehide', this.stashFile, false);
  },

  // TODO: We might not wanna use this
  parseMetadata: function(metadata) {
    var metadata = this.metadataEditor.getValue();
    if (!metadata) return {};
    try {
      return jsyaml.load(metadata);
    } catch(err) {
      return null;
    }
  },

  updateMetaData: function() {
    if (!this.model.jekyll) return true; // metadata -> skip

    // Update published
    function updatePublished(yamlStr, published) {
      var regex = /published: (false|true)/;
      if (yamlStr.match(regex)) {
        return yamlStr.replace(regex, "published: " + !!published);
      } else {
        return yamlStr + "\npublished: " + !!published;
      }
    }

    this.model.raw_metadata = this.metadataEditor.getValue();
    var published = this.$('#post_published').prop('checked');

    this.model.raw_metadata = updatePublished(this.model.raw_metadata, published);
    console.log(this.model.raw_metadata);
    this.metadataEditor.setValue(this.model.raw_metadata);

    published ? $('#post').addClass('published') : $('#post').removeClass('published');

    return true;
  },

  updateFilename: function(filepath, cb) {
    var that = this;

    if (!_.validPathname(filepath)) return cb('error');
    app.state.path = this.model.path; // ?
    app.state.file = _.extractFilename(filepath)[1];
    app.state.path = _.extractFilename(filepath)[0];

    function finish() {
      that.model.path = app.state.path;
      that.model.file = app.state.file;
      // rerender header to reflect the filename change
      app.instance.header.render();
      that.updateURL();
    }

    if (this.model.persisted) {
      movePost(app.state.user, app.state.repo, app.state.branch, _.filepath(this.model.path, this.model.file), filepath, _.bind(function(err) {
        if (!err) finish()
        err ? cb('error') : cb(null)
      }, this));
    } else {
      finish();
      cb(null);
    }
  },

  serialize: function() {
    return serialize(this.model.content, this.model.jekyll ? this.model.raw_metadata : null)
  },

  // Update save state (saving ..., sending patch ..., etc.)

  updateSaveState: function(label, classes) {
    $('.button.save').html(label)
                     .removeClass('inactive error saving')
                     .addClass(classes);
  },

  // Submits a patch (fork + pull request workflow)

  sendPatch: function(filepath, filename, filecontent, message) {
    var that = this;

    function patch() {
      if (that.updateMetaData()) {
        that.model.content = that.prevContent;
        console.log(that.prevContent);
        that.editor.setValue(that.prevContent);

        patchFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() {
              that.$('.button.save').html("SUBMIT CHANGE");
              that.$('.button.save').removeClass('error');
              that.$('.button.save').addClass('inactive');
            }, 3000);
            that.updateSaveState('! Try again in 30 seconds', 'error');
            return;
          }

          that.dirty = false;
          that.model.persisted = true;
          that.model.file = filename;
          that.updateURL();
          that.prevContent = filecontent;
          that.updateSaveState('CHANGE SUBMITTED', 'inactive');
        });
      } else {
        that.updateSaveState('! Metadata', 'error');
      }
    }

    that.updateSaveState('SUBMITTING CHANGE ...', 'inactive saving');
    patch();

    return false;
  },

  saveFile: function(filepath, filename, filecontent, message) {
    var that = this;

    function save() {
      if (that.updateMetaData()) {
        saveFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() { that._makeDirty() }, 3000);
            that.updateSaveState('! Try again in 30 seconds', 'error');
            return;
          }
          that.dirty = false;
          that.model.persisted = true;
          that.model.file = filename;
          that.updateURL();
          that.prevContent = filecontent;
          that.updateSaveState('SAVED', 'inactive');
        });
      } else {
        that.updateSaveState('! Metadata', 'error');
      }
    }

    that.updateSaveState('SAVING ...', 'inactive saving');

    if (filepath === _.filepath(this.model.path, this.model.file)) return save();

    // Move or create file
    this.updateFilename(filepath, function(err) {
      err ? that.updateSaveState('! Filename', 'error') : save();
    });
  },

  stashFile: function(event) {
    event.preventDefault();

    console.log(event, window.localStorage);
    alert(event);

    if (!window.localStorage) return false;

    var storage = window.localStorage,
        filepath = $('input.filepath').val(),
        filecontent = this.serialize();

    storage.setItem(filepath, JSON.stringify({
      sha: app.state.sha,
      content: this.editor ? this.editor.getValue() : null,
      raw_metadata: this.model.jekyll && this.metadataEditor ? this.metadataEditor.getValue() : null
    }));
  },

  stashApply: function() {
    if (!window.localStorage) return false;

    var storage = window.localStorage,
        filepath = $('input.filepath').val();

    var stash = JSON.parse(storage.getItem(filepath));

    if (stash && stash.sha === app.state.sha) {
      if (this.editor) this.editor.setValue(stash.content);
      if (this.metadataEditor) this.metadataEditor.setValue(stash.raw_metadata);
    } else {
      storage.delItem(filepath);
    }
  },

  updateFile: function() {
    var that = this,
        filepath = $('input.filepath').val(),
        filename = _.extractFilename(filepath)[1],
        filecontent = this.serialize(),
        message = this.$('.commit-message').val() || this.$('.commit-message').attr('placeholder'),
        method = this.model.writeable ? this.saveFile : this.sendPatch;

    // Update content
    this.model.content = this.editor.getValue();

    // Delegate
    method.call(this, filepath, filename, filecontent, message);
  },

  keyMap: function() {
    var that = this;
    return {
      "Shift-Ctrl-Left": function(codemirror) {
        that.left();
      },
      "Shift-Ctrl-Right": function(codemirror) {
        that.right();
      },
      "Shift-Ctrl-M": function(codemirror) {
        that._toggleMeta();
      },
      "Ctrl-S": function(codemirror) {
        that.updateFile();
      }
    };
  },

  initEditor: function() {
    var that = this;
    setTimeout(function() {
      if (that.model.jekyll) {
        that.metadataEditor = CodeMirror($('#raw_metadata')[0], {
          mode: 'yaml',
          value: that.model.raw_metadata,
          theme: 'prose-dark',
          lineWrapping: true,
          lineNumbers: true,
          extraKeys: that.keyMap(),
          onChange: _.bind(that._makeDirty, that)
        });
        $('#post .metadata').hide();
      }
      that.editor = CodeMirror($('#code')[0], {
        mode: that.model.lang,
        value: that.model.content,
        lineWrapping: true,
        lineNumbers: true,
        extraKeys: that.keyMap(),
        matchBrackets: true,
        theme: 'prose-bright',
        onChange: _.bind(that._makeDirty, that)
      });
      that.refreshCodeMirror();
    }, 100);
  },

  render: function() {
    var that = this;
    $(this.el).html(templates.post(_.extend(this.model, { mode: this.mode })));
    if (this.model.published) $(this.el).addClass('published');
    this.initEditor();
    return this;
  }
});

}).apply(this, window.args);
<commit_msg>stash on pagehide if dirty, restore on initEditor<commit_after>(function(config, models, views, routers, utils, templates) {

views.Post = Backbone.View.extend({

  id: 'post',

  events: {
    'click .save': '_save',
    'click .cancel-save': '_toggleCommit',
    'click .save.confirm': 'updateFile',
    'click a.toggle.view': '_toggleView',
    'click a.toggle.meta': '_toggleMeta',
    'change input': '_makeDirty',
    'change #post_published': 'updateMetaData',
    'click .delete': '_delete',
    'click .toggle-options': '_toggleOptions'
  },

  _toggleOptions: function() {
    $('.options').toggle();
    return false;
  },

  _delete: function() {
    if (confirm("Are you sure you want to delete that file?")) {
      deletePost(app.state.user, app.state.repo, app.state.branch, this.model.path, this.model.file, _.bind(function(err) {
        if (err) return alert('Error during deletion. Please wait 30 seconds and try again.');
        router.navigate([app.state.user, app.state.repo, "tree", app.state.branch].join('/'), true);
      }, this));
    }
    return false;
  },

  updateURL: function() {
    var url = _.compact([app.state.user, app.state.repo, this.model.preview ? "blob" : "edit", app.state.branch, this.model.path, this.model.file]);
    router.navigate(url.join('/'), false);
  },

  _makeDirty: function(e) {
    this.dirty = true;
    if (this.editor) this.model.content = this.editor.getValue();
    if (this.metadataEditor) this.model.raw_metadata = this.metadataEditor.getValue();
    if (!this.$('.button.save').hasClass('saving')) {
      this.$('.button.save').html(this.model.writeable ? "SAVE" : "SUBMIT CHANGE");
      this.$('.button.save').removeClass('inactive error');
    }
  },

  showDiff: function() {
    var text1 = this.model.persisted ? this.prevContent : '';
    var text2 = this.serialize();
    var d = this.dmp.diff_main(text1, text2);
    this.dmp.diff_cleanupSemantic(d);
    var diff = this.dmp.diff_prettyHtml(d).replace(/¶/g, "");
    $('.diff-wrapper .diff').html(diff);
  },

  _toggleCommit: function() {
    if (!this.$('.document-menu').hasClass('commit')) {
      this.$('.commit-message').attr( 'placeholder', "Updated "+$('input.filepath').val());
    }

    this.hideMeta();
    this.$('.button.save').html(this.$('.document-menu').hasClass('commit') ? (this.model.writeable ? "SAVE" : "SUBMIT CHANGE") : "COMMIT");
    this.$('.button.save').toggleClass('confirm');
    this.$('.document-menu').toggleClass('commit');
    this.$('.button.cancel-save').toggle();
    this.$('.document-menu-content .options').hide();
    this.showDiff();
    this.$('.surface').toggle();
    this.$('.diff-wrapper').toggle();
    this.$('.commit-message').focus();

    return false;
  },

  _save: function(e) {
    if (!this.dirty) return false;
    this._toggleCommit();
    e.preventDefault();
    return false;
  },

  _toggleView: function(e) {
    var that = this;
    this.toggleView($(e.currentTarget).attr('data-view'));
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  _toggleMeta: function(e) {
    var that = this;
    if (e) e.preventDefault();
    $('.toggle.meta').toggleClass('active');
    $('.metadata').toggle();
    _.delay(function() { that.refreshCodeMirror(); }, 1);
    return false;
  },

  refreshCodeMirror: function() {
    if ($('.toggle.meta').hasClass('active')) {
      $('.CodeMirror-scroll').height($('.document').height() / 2);
    } else {
      $('.CodeMirror-scroll').height($('.document').height());
    }
    this.editor.refresh();
    if (this.metadataEditor) this.metadataEditor.refresh();
  },

  toggleView: function(view) {
    this.view = view;
    if (view === 'preview') {
      this.model.preview = true;
      this.$('.post-content').html(marked(this.model.content));
    } else {
      this.model.preview = false;
    }
    this.hideMeta();
    this.updateURL();
    $('.toggle').removeClass('active');
    $('.toggle.'+view).addClass('active');

    $('.document .surface').removeClass('preview cheatsheet compose');
    $('.document .surface').addClass(view);
  },

  hideMeta: function() {
    $('.toggle.meta').removeClass('active');
    $('.metadata').hide();
  },

  right: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'preview') return;
    if (view === 'compose') return this.toggleView('preview');
    return this.toggleView('compose');
  },

  left: function() {
    var view = $('.toggle.active').attr('data-view');
    if (view === 'cheatsheet') return;
    if (view === 'compose') return this.toggleView('cheatsheet');
    return this.toggleView('compose');
  },

  initialize: function() {
    this.dmp = new diff_match_patch();
    this.mode = "edit";
    this.prevContent = this.serialize();
    if (!window.shortcutsRegistered) {
      key('⌘+s, ctrl+s', _.bind(function() { this.updateFile(); return false; }, this));
      key('ctrl+shift+right', _.bind(function() { this.right(); return false; }, this));
      key('ctrl+shift+left', _.bind(function() { this.left(); return false; }, this));
      key('esc', _.bind(function() { this.toggleView('compose'); return false; }, this));
      window.shortcutsRegistered = true;
    }

    // Stash editor and metadataEditor content to localStorage on pagehide event
    // Always run stashFile in context of view
    $(window).on('pagehide', _.bind(this.stashFile, this));
  },

  // TODO: We might not wanna use this
  parseMetadata: function(metadata) {
    var metadata = this.metadataEditor.getValue();
    if (!metadata) return {};
    try {
      return jsyaml.load(metadata);
    } catch(err) {
      return null;
    }
  },

  updateMetaData: function() {
    if (!this.model.jekyll) return true; // metadata -> skip

    // Update published
    function updatePublished(yamlStr, published) {
      var regex = /published: (false|true)/;
      if (yamlStr.match(regex)) {
        return yamlStr.replace(regex, "published: " + !!published);
      } else {
        return yamlStr + "\npublished: " + !!published;
      }
    }

    this.model.raw_metadata = this.metadataEditor.getValue();
    var published = this.$('#post_published').prop('checked');

    this.model.raw_metadata = updatePublished(this.model.raw_metadata, published);
    this.metadataEditor.setValue(this.model.raw_metadata);

    published ? $('#post').addClass('published') : $('#post').removeClass('published');

    return true;
  },

  updateFilename: function(filepath, cb) {
    var that = this;

    if (!_.validPathname(filepath)) return cb('error');
    app.state.path = this.model.path; // ?
    app.state.file = _.extractFilename(filepath)[1];
    app.state.path = _.extractFilename(filepath)[0];

    function finish() {
      that.model.path = app.state.path;
      that.model.file = app.state.file;
      // rerender header to reflect the filename change
      app.instance.header.render();
      that.updateURL();
    }

    if (this.model.persisted) {
      movePost(app.state.user, app.state.repo, app.state.branch, _.filepath(this.model.path, this.model.file), filepath, _.bind(function(err) {
        if (!err) finish()
        err ? cb('error') : cb(null)
      }, this));
    } else {
      finish();
      cb(null);
    }
  },

  serialize: function() {
    return serialize(this.model.content, this.model.jekyll ? this.model.raw_metadata : null)
  },

  // Update save state (saving ..., sending patch ..., etc.)

  updateSaveState: function(label, classes) {
    $('.button.save').html(label)
                     .removeClass('inactive error saving')
                     .addClass(classes);
  },

  // Submits a patch (fork + pull request workflow)

  sendPatch: function(filepath, filename, filecontent, message) {
    var that = this;

    function patch() {
      if (that.updateMetaData()) {
        that.model.content = that.prevContent;
        that.editor.setValue(that.prevContent);

        patchFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() {
              that.$('.button.save').html("SUBMIT CHANGE");
              that.$('.button.save').removeClass('error');
              that.$('.button.save').addClass('inactive');
            }, 3000);
            that.updateSaveState('! Try again in 30 seconds', 'error');
            return;
          }

          that.dirty = false;
          that.model.persisted = true;
          that.model.file = filename;
          that.updateURL();
          that.prevContent = filecontent;
          that.updateSaveState('CHANGE SUBMITTED', 'inactive');
        });
      } else {
        that.updateSaveState('! Metadata', 'error');
      }
    }

    that.updateSaveState('SUBMITTING CHANGE ...', 'inactive saving');
    patch();

    return false;
  },

  saveFile: function(filepath, filename, filecontent, message) {
    var that = this;

    function save() {
      if (that.updateMetaData()) {
        saveFile(app.state.user, app.state.repo, app.state.branch, filepath, filecontent, message, function(err) {
          if (err) {
            _.delay(function() { that._makeDirty() }, 3000);
            that.updateSaveState('! Try again in 30 seconds', 'error');
            return;
          }
          that.dirty = false;
          that.model.persisted = true;
          that.model.file = filename;
          that.updateURL();
          that.prevContent = filecontent;
          that.updateSaveState('SAVED', 'inactive');
        });
      } else {
        that.updateSaveState('! Metadata', 'error');
      }
    }

    that.updateSaveState('SAVING ...', 'inactive saving');

    if (filepath === _.filepath(this.model.path, this.model.file)) return save();

    // Move or create file
    this.updateFilename(filepath, function(err) {
      err ? that.updateSaveState('! Filename', 'error') : save();
    });
  },

  stashFile: function(event) {
    // Only stash dirty files
    if (!window.localStorage || !this.dirty) return false;

    var storage = window.localStorage,
        filepath = $('input.filepath').val();

    // Don't stash if filepath is undefined
    if (filepath) {
      storage.setItem(filepath, JSON.stringify({
        sha: app.state.sha,
        content: this.editor ? this.editor.getValue() : null,
        raw_metadata: this.model.jekyll && this.metadataEditor ? this.metadataEditor.getValue() : null
      }));
    }
  },

  stashApply: function() {
    if (!window.localStorage) return false;

    var storage = window.localStorage,
        filepath = $('input.filepath').val();

    var item = storage.getItem(filepath);
    var stash = JSON.parse(item);

    if (stash && stash.sha === window.app.state.sha) {
      // Restore from stash if file sha hasn't changed
      if (this.editor) this.editor.setValue(stash.content);
      if (this.metadataEditor) this.metadataEditor.setValue(stash.raw_metadata);
    } else if (item) {
      // Remove expired content
      storage.removeItem(filepath);
    }
  },

  updateFile: function() {
    var that = this,
        filepath = $('input.filepath').val(),
        filename = _.extractFilename(filepath)[1],
        filecontent = this.serialize(),
        message = this.$('.commit-message').val() || this.$('.commit-message').attr('placeholder'),
        method = this.model.writeable ? this.saveFile : this.sendPatch;

    // Update content
    this.model.content = this.editor.getValue();

    // Delegate
    method.call(this, filepath, filename, filecontent, message);
  },

  keyMap: function() {
    var that = this;
    return {
      "Shift-Ctrl-Left": function(codemirror) {
        that.left();
      },
      "Shift-Ctrl-Right": function(codemirror) {
        that.right();
      },
      "Shift-Ctrl-M": function(codemirror) {
        that._toggleMeta();
      },
      "Ctrl-S": function(codemirror) {
        that.updateFile();
      }
    };
  },

  initEditor: function() {
    var that = this;
    setTimeout(function() {
      if (that.model.jekyll) {
        that.metadataEditor = CodeMirror($('#raw_metadata')[0], {
          mode: 'yaml',
          value: that.model.raw_metadata,
          theme: 'prose-dark',
          lineWrapping: true,
          lineNumbers: true,
          extraKeys: that.keyMap(),
          onChange: _.bind(that._makeDirty, that)
        });
        $('#post .metadata').hide();
      }
      that.editor = CodeMirror($('#code')[0], {
        mode: that.model.lang,
        value: that.model.content,
        lineWrapping: true,
        lineNumbers: true,
        extraKeys: that.keyMap(),
        matchBrackets: true,
        theme: 'prose-bright',
        onChange: _.bind(that._makeDirty, that)
      });
      that.refreshCodeMirror();

      // Check localStorage for existing stash
      // Apply if stash exists and is current, remove if expired
      that.stashApply();
    }, 100);
  },

  render: function() {
    var that = this;
    $(this.el).html(templates.post(_.extend(this.model, { mode: this.mode })));
    if (this.model.published) $(this.el).addClass('published');
    this.initEditor();
    return this;
  },

  remove: function() {
    // Unbind pagehide event handler when View is removed
    $(window).unbind('pagehide');
    Backbone.View.prototype.remove.call(this);
  }
});

}).apply(this, window.args);
<|endoftext|>


<commit_before>import {registerBidder} from '../src/adapters/bidderFactory.js';
import * as utils from '../src/utils.js';
import {config} from '../src/config.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';

const BIDDER_CODE = 'aso';
const DEFAULT_SERVER_URL = 'https://srv.aso1.net';
const DEFAULT_SERVER_PATH = '/prebid/bidder';
const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
const TTL = 300;

export const spec = {

  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, VIDEO],

  isBidRequestValid: bid => {
    return !!bid.params && !!bid.params.zone;
  },

  buildRequests: (validBidRequests, bidderRequest) => {
    let serverRequests = [];

    utils._each(validBidRequests, bidRequest => {
      const payload = createBasePayload(bidRequest, bidderRequest);

      const bannerParams = utils.deepAccess(bidRequest, 'mediaTypes.banner');
      const videoParams = utils.deepAccess(bidRequest, 'mediaTypes.video');

      let imp;

      if (bannerParams && videoParams) {
        utils.logWarn('Please note, multiple mediaTypes are not supported. The only banner will be used.')
      }

      if (bannerParams) {
        imp = createBannerImp(bidRequest, bannerParams)
      } else if (videoParams) {
        imp = createVideoImp(bidRequest, videoParams)
      }

      if (imp) {
        payload.imp.push(imp);
      } else {
        return;
      }

      serverRequests.push({
        method: 'POST',
        url: getEnpoint(bidRequest),
        data: payload,
        options: {
          withCredentials: true,
          crossOrigin: true
        },
        bidRequest: bidRequest
      });
    });

    return serverRequests;
  },

  interpretResponse: (serverResponse, {bidRequest}) => {
    const response = serverResponse && serverResponse.body;

    if (!response) {
      return [];
    }

    const serverBids = response.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []);
    const serverBid = serverBids[0];

    let bids = [];

    const bid = {
      requestId: serverBid.impid,
      cpm: serverBid.price,
      width: serverBid.w,
      height: serverBid.h,
      ttl: TTL,
      creativeId: serverBid.crid,
      netRevenue: true,
      currency: response.cur,
      mediaType: bidRequest.mediaType,
      meta: {
        mediaType: bidRequest.mediaType,
        advertiserDomains: serverBid.adomain ? serverBid.adomain : []
      }
    };

    if (bid.mediaType === BANNER) {
      bid.ad = serverBid.adm;
    } else if (bid.mediaType === VIDEO) {
      bid.vastXml = serverBid.adm;
      if (utils.deepAccess(bidRequest, 'mediaTypes.video.context') === 'outstream') {
        bid.adResponse = {
          content: bid.vastXml,
        };
        bid.renderer = createRenderer(bidRequest, OUTSTREAM_RENDERER_URL);
      }
    }

    bids.push(bid);

    return bids;
  },

  getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) {
    const urls = [];

    if (serverResponses && serverResponses.length !== 0) {
      let query = '';
      if (gdprConsent) {
        query = utils.tryAppendQueryString(query, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0));
        query = utils.tryAppendQueryString(query, 'consents_str', gdprConsent.consentString);
        const consentsIds = getConsentsIds(gdprConsent);
        if (consentsIds) {
          query = utils.tryAppendQueryString(query, 'consents', consentsIds);
        }
      }

      if (uspConsent) {
        query = utils.tryAppendQueryString(query, 'us_privacy', uspConsent);
      }

      utils._each(serverResponses, resp => {
        const userSyncs = utils.deepAccess(resp, 'body.ext.user_syncs');
        if (!userSyncs) {
          return;
        }

        utils._each(userSyncs, us => {
          urls.push({
            type: us.type,
            url: us.url + (query ? '?' + query : '')
          });
        });
      });
    }

    return urls;
  }
};

function outstreamRender(bid) {
  bid.renderer.push(() => {
    window.ANOutstreamVideo.renderAd({
      sizes: [bid.width, bid.height],
      targetId: bid.adUnitCode,
      adResponse: bid.adResponse,
      rendererOptions: bid.renderer.getConfig()
    });
  });
}

function createRenderer(bid, url) {
  const renderer = Renderer.install({
    id: bid.bidId,
    url: url,
    loaded: false,
    config: utils.deepAccess(bid, 'renderer.options'),
    adUnitCode: bid.adUnitCode
  });
  renderer.setRender(outstreamRender);
  return renderer;
}

function getUrlsInfo(bidderRequest) {
  let page = '';
  let referrer = '';

  const {refererInfo} = bidderRequest;

  if (utils.inIframe()) {
    page = refererInfo.referer;
  } else {
    const w = utils.getWindowTop();
    page = w.location.href;
    referrer = w.document.referrer || '';
  }

  page = config.getConfig('pageUrl') || page;
  const url = utils.parseUrl(page);
  const domain = url.hostname;

  return {
    domain,
    page,
    referrer
  };
}

function getSize(paramSizes) {
  const parsedSizes = utils.parseSizesInput(paramSizes);
  const sizes = parsedSizes.map(size => {
    const [width, height] = size.split('x');
    const w = parseInt(width, 10);
    const h = parseInt(height, 10);
    return {w, h};
  });

  return sizes[0] || null;
}

function getBidFloor(bidRequest, size) {
  if (!utils.isFn(bidRequest.getFloor)) {
    return null;
  }

  const bidFloor = bidRequest.getFloor({
    mediaType: bidRequest.mediaType,
    size: size ? [size.w, size.h] : '*'
  });

  if (!isNaN(bidFloor.floor)) {
    return bidFloor;
  }

  return null;
}

function createBaseImp(bidRequest, size) {
  const imp = {
    id: bidRequest.bidId,
    tagid: bidRequest.adUnitCode,
    secure: 1
  };

  const bidFloor = getBidFloor(bidRequest, size);
  if (bidFloor !== null) {
    imp.bidfloor = bidFloor.floor;
    imp.bidfloorcur = bidFloor.currency;
  }

  return imp;
}

function createBannerImp(bidRequest, bannerParams) {
  bidRequest.mediaType = BANNER;

  const size = getSize(bannerParams.sizes);
  const imp = createBaseImp(bidRequest, size);

  imp.banner = {
    w: size.w,
    h: size.h,
    topframe: utils.inIframe() ? 0 : 1
  }

  return imp;
}

function createVideoImp(bidRequest, videoParams) {
  bidRequest.mediaType = VIDEO;
  const size = getSize(videoParams.playerSize);
  const imp = createBaseImp(bidRequest, size);

  imp.video = {
    mimes: videoParams.mimes,
    minduration: videoParams.minduration,
    startdelay: videoParams.startdelay,
    linearity: videoParams.linearity,
    maxduration: videoParams.maxduration,
    skip: videoParams.skip,
    protocols: videoParams.protocols,
    skipmin: videoParams.skipmin,
    api: videoParams.api
  }

  if (size) {
    imp.video.w = size.w;
    imp.video.h = size.h;
  }

  return imp;
}

function getEnpoint(bidRequest) {
  const serverUrl = bidRequest.params.serverUrl || DEFAULT_SERVER_URL;
  const serverPath = bidRequest.params.serverPath || DEFAULT_SERVER_PATH;

  return serverUrl + serverPath + '?zid=' + bidRequest.params.zone + '&pbjs=$prebid.version$';
}

function getConsentsIds(gdprConsent) {
  const consents = utils.deepAccess(gdprConsent, 'vendorData.purpose.consents', []);
  let consentsIds = [];
  for (const [key, value] of Object.entries(consents)) {
    if (value === true) {
      consentsIds.push(key);
    }
  }
  return consentsIds.join(',');
}

function createBasePayload(bidRequest, bidderRequest) {
  const urlsInfo = getUrlsInfo(bidderRequest);

  const payload = {
    id: bidRequest.auctionId + '_' + bidRequest.bidId,
    at: 1,
    tmax: bidderRequest.timeout,
    site: {
      id: urlsInfo.domain,
      domain: urlsInfo.domain,
      page: urlsInfo.page,
      ref: urlsInfo.referrer
    },
    device: {
      dnt: utils.getDNT() ? 1 : 0,
      h: window.innerHeight,
      w: window.innerWidth,
    },
    imp: [],
    ext: {},
    user: {}
  };

  if (bidRequest.params.attr) {
    utils.deepSetValue(payload, 'site.ext.attr', bidRequest.params.attr);
  }

  if (bidderRequest.gdprConsent) {
    utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString);
    const consentsIds = getConsentsIds(bidderRequest.gdprConsent);
    if (consentsIds) {
      utils.deepSetValue(payload, 'user.ext.consents', consentsIds);
    }
    utils.deepSetValue(payload, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1);
  }

  if (bidderRequest.uspConsent) {
    utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent);
  }

  if (config.getConfig('coppa')) {
    utils.deepSetValue(payload, 'regs.coppa', 1);
  }

  const eids = utils.deepAccess(bidRequest, 'userIdAsEids');
  if (eids && eids.length) {
    utils.deepSetValue(payload, 'user.ext.eids', eids);
  }

  return payload;
}

registerBidder(spec);
<commit_msg>Object.entries replaced with backward compatible version (#7122)<commit_after>import {registerBidder} from '../src/adapters/bidderFactory.js';
import * as utils from '../src/utils.js';
import {config} from '../src/config.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';

const BIDDER_CODE = 'aso';
const DEFAULT_SERVER_URL = 'https://srv.aso1.net';
const DEFAULT_SERVER_PATH = '/prebid/bidder';
const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
const TTL = 300;

export const spec = {

  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER, VIDEO],

  isBidRequestValid: bid => {
    return !!bid.params && !!bid.params.zone;
  },

  buildRequests: (validBidRequests, bidderRequest) => {
    let serverRequests = [];

    utils._each(validBidRequests, bidRequest => {
      const payload = createBasePayload(bidRequest, bidderRequest);

      const bannerParams = utils.deepAccess(bidRequest, 'mediaTypes.banner');
      const videoParams = utils.deepAccess(bidRequest, 'mediaTypes.video');

      let imp;

      if (bannerParams && videoParams) {
        utils.logWarn('Please note, multiple mediaTypes are not supported. The only banner will be used.')
      }

      if (bannerParams) {
        imp = createBannerImp(bidRequest, bannerParams)
      } else if (videoParams) {
        imp = createVideoImp(bidRequest, videoParams)
      }

      if (imp) {
        payload.imp.push(imp);
      } else {
        return;
      }

      serverRequests.push({
        method: 'POST',
        url: getEnpoint(bidRequest),
        data: payload,
        options: {
          withCredentials: true,
          crossOrigin: true
        },
        bidRequest: bidRequest
      });
    });

    return serverRequests;
  },

  interpretResponse: (serverResponse, {bidRequest}) => {
    const response = serverResponse && serverResponse.body;

    if (!response) {
      return [];
    }

    const serverBids = response.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []);
    const serverBid = serverBids[0];

    let bids = [];

    const bid = {
      requestId: serverBid.impid,
      cpm: serverBid.price,
      width: serverBid.w,
      height: serverBid.h,
      ttl: TTL,
      creativeId: serverBid.crid,
      netRevenue: true,
      currency: response.cur,
      mediaType: bidRequest.mediaType,
      meta: {
        mediaType: bidRequest.mediaType,
        advertiserDomains: serverBid.adomain ? serverBid.adomain : []
      }
    };

    if (bid.mediaType === BANNER) {
      bid.ad = serverBid.adm;
    } else if (bid.mediaType === VIDEO) {
      bid.vastXml = serverBid.adm;
      if (utils.deepAccess(bidRequest, 'mediaTypes.video.context') === 'outstream') {
        bid.adResponse = {
          content: bid.vastXml,
        };
        bid.renderer = createRenderer(bidRequest, OUTSTREAM_RENDERER_URL);
      }
    }

    bids.push(bid);

    return bids;
  },

  getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) {
    const urls = [];

    if (serverResponses && serverResponses.length !== 0) {
      let query = '';
      if (gdprConsent) {
        query = utils.tryAppendQueryString(query, 'gdpr', (gdprConsent.gdprApplies ? 1 : 0));
        query = utils.tryAppendQueryString(query, 'consents_str', gdprConsent.consentString);
        const consentsIds = getConsentsIds(gdprConsent);
        if (consentsIds) {
          query = utils.tryAppendQueryString(query, 'consents', consentsIds);
        }
      }

      if (uspConsent) {
        query = utils.tryAppendQueryString(query, 'us_privacy', uspConsent);
      }

      utils._each(serverResponses, resp => {
        const userSyncs = utils.deepAccess(resp, 'body.ext.user_syncs');
        if (!userSyncs) {
          return;
        }

        utils._each(userSyncs, us => {
          urls.push({
            type: us.type,
            url: us.url + (query ? '?' + query : '')
          });
        });
      });
    }

    return urls;
  }
};

function outstreamRender(bid) {
  bid.renderer.push(() => {
    window.ANOutstreamVideo.renderAd({
      sizes: [bid.width, bid.height],
      targetId: bid.adUnitCode,
      adResponse: bid.adResponse,
      rendererOptions: bid.renderer.getConfig()
    });
  });
}

function createRenderer(bid, url) {
  const renderer = Renderer.install({
    id: bid.bidId,
    url: url,
    loaded: false,
    config: utils.deepAccess(bid, 'renderer.options'),
    adUnitCode: bid.adUnitCode
  });
  renderer.setRender(outstreamRender);
  return renderer;
}

function getUrlsInfo(bidderRequest) {
  let page = '';
  let referrer = '';

  const {refererInfo} = bidderRequest;

  if (utils.inIframe()) {
    page = refererInfo.referer;
  } else {
    const w = utils.getWindowTop();
    page = w.location.href;
    referrer = w.document.referrer || '';
  }

  page = config.getConfig('pageUrl') || page;
  const url = utils.parseUrl(page);
  const domain = url.hostname;

  return {
    domain,
    page,
    referrer
  };
}

function getSize(paramSizes) {
  const parsedSizes = utils.parseSizesInput(paramSizes);
  const sizes = parsedSizes.map(size => {
    const [width, height] = size.split('x');
    const w = parseInt(width, 10);
    const h = parseInt(height, 10);
    return {w, h};
  });

  return sizes[0] || null;
}

function getBidFloor(bidRequest, size) {
  if (!utils.isFn(bidRequest.getFloor)) {
    return null;
  }

  const bidFloor = bidRequest.getFloor({
    mediaType: bidRequest.mediaType,
    size: size ? [size.w, size.h] : '*'
  });

  if (!isNaN(bidFloor.floor)) {
    return bidFloor;
  }

  return null;
}

function createBaseImp(bidRequest, size) {
  const imp = {
    id: bidRequest.bidId,
    tagid: bidRequest.adUnitCode,
    secure: 1
  };

  const bidFloor = getBidFloor(bidRequest, size);
  if (bidFloor !== null) {
    imp.bidfloor = bidFloor.floor;
    imp.bidfloorcur = bidFloor.currency;
  }

  return imp;
}

function createBannerImp(bidRequest, bannerParams) {
  bidRequest.mediaType = BANNER;

  const size = getSize(bannerParams.sizes);
  const imp = createBaseImp(bidRequest, size);

  imp.banner = {
    w: size.w,
    h: size.h,
    topframe: utils.inIframe() ? 0 : 1
  }

  return imp;
}

function createVideoImp(bidRequest, videoParams) {
  bidRequest.mediaType = VIDEO;
  const size = getSize(videoParams.playerSize);
  const imp = createBaseImp(bidRequest, size);

  imp.video = {
    mimes: videoParams.mimes,
    minduration: videoParams.minduration,
    startdelay: videoParams.startdelay,
    linearity: videoParams.linearity,
    maxduration: videoParams.maxduration,
    skip: videoParams.skip,
    protocols: videoParams.protocols,
    skipmin: videoParams.skipmin,
    api: videoParams.api
  }

  if (size) {
    imp.video.w = size.w;
    imp.video.h = size.h;
  }

  return imp;
}

function getEnpoint(bidRequest) {
  const serverUrl = bidRequest.params.serverUrl || DEFAULT_SERVER_URL;
  const serverPath = bidRequest.params.serverPath || DEFAULT_SERVER_PATH;

  return serverUrl + serverPath + '?zid=' + bidRequest.params.zone + '&pbjs=$prebid.version$';
}

function getConsentsIds(gdprConsent) {
  const consents = utils.deepAccess(gdprConsent, 'vendorData.purpose.consents', []);
  let consentsIds = [];

  Object.keys(consents).forEach(function (key) {
    if (consents[key] === true) {
      consentsIds.push(key);
    }
  });

  return consentsIds.join(',');
}

function createBasePayload(bidRequest, bidderRequest) {
  const urlsInfo = getUrlsInfo(bidderRequest);

  const payload = {
    id: bidRequest.auctionId + '_' + bidRequest.bidId,
    at: 1,
    tmax: bidderRequest.timeout,
    site: {
      id: urlsInfo.domain,
      domain: urlsInfo.domain,
      page: urlsInfo.page,
      ref: urlsInfo.referrer
    },
    device: {
      dnt: utils.getDNT() ? 1 : 0,
      h: window.innerHeight,
      w: window.innerWidth,
    },
    imp: [],
    ext: {},
    user: {}
  };

  if (bidRequest.params.attr) {
    utils.deepSetValue(payload, 'site.ext.attr', bidRequest.params.attr);
  }

  if (bidderRequest.gdprConsent) {
    utils.deepSetValue(payload, 'user.ext.consent', bidderRequest.gdprConsent.consentString);
    const consentsIds = getConsentsIds(bidderRequest.gdprConsent);
    if (consentsIds) {
      utils.deepSetValue(payload, 'user.ext.consents', consentsIds);
    }
    utils.deepSetValue(payload, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1);
  }

  if (bidderRequest.uspConsent) {
    utils.deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent);
  }

  if (config.getConfig('coppa')) {
    utils.deepSetValue(payload, 'regs.coppa', 1);
  }

  const eids = utils.deepAccess(bidRequest, 'userIdAsEids');
  if (eids && eids.length) {
    utils.deepSetValue(payload, 'user.ext.eids', eids);
  }

  return payload;
}

registerBidder(spec);
<|endoftext|>


<commit_before>const fs = require("fs");
const http = require("http");
const https = require("https");
const Discord = require("discord.js");
const child_process = require("child_process");
const createHandler = require("github-webhook-handler");
const handler = createHandler({path: global.config.webhook.path, secret: global.config.webhook.secret});

//El servidor que se encarga de recibir las POST requests de github (webhooks)
const server = http.createServer((req, res) => {
    let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress ||
            req.socket.remoteAddress || req.connection.socket.remoteAddress;
    console.log(`[${new Date().toLocaleString()}] Incoming request: \n\tClient IP: ${ip}\n\tPath: ${req.url}\n\tMethod: ${req.method}`);
    handler(req, res, (err) => {
        res.statusCode = 404;
        res.end("No such file or directory.");
    });
});

//Evita arrancar el servidor http en caso de que travis tenga el control
if(global.config.version !== 'travis')
    server.listen(global.config.webhook.port);

//Evento se ejecuta cuando sale una release del bot
handler.on("release", (evt) => {
    let newVersion = evt.payload.release.tag_name;
    console.log("UPDATING!");
    //Asegurarnos de que el bot esta `ready`
    if(global.bot.readyAt) {
        global.bot.user.setGame("Actualizando a " + newVersion);
    }
    //cerrar el servidor http para que el nuevo bot pueda escuchar el mismo puerto.
    server.close();

    //Clonar la nueva version a una carpeta paralela
    child_process.execSync("git clone https:\/\/github.com/darkaqua/darkbot ../" + newVersion);
    process.chdir("../" + newVersion)
    //Instalar dependencias
    child_process.execSync("npm install");

    //Abrir archivo para el log
    const stdio = fs.openSync("out.log", "a");

    //Destruir el bot actual y ejecutar el nuevo.
    child_process.spawn("node", ["main.js", newVersion, global.config.version], { detached: true, stdio: ["ignore", stdio, stdio] });
    process.exit();
});

handler.on("error", (err) => {
    console.log("Webhook Handler Error: " + err.message);
});

global.bot.on("ready", () => {
    if(global.config.oldVersion) {
        //Avisar con embed de que se ha actualizado.
        //Pide info sobre la nueva version a la api de github.
        let options = {
            hostname: "api.github.com",
            path: `/repos/darkaqua/darkbot/releases/tags/${global.config.version}`,
            headers: { "User-Agent": "darkaqua-darkbot" }
        };
        let req = https.request(options, (res) => {
            let data = "";
            res.on("data", chunk => { data += chunk; });
            res.on("end", () => {
                if(res.statusCode !== 404) {
                    let release = JSON.parse(data);
                    let type = release["prerelease"] ? "pre-release" : "release";
                    let embed = new Discord.RichEmbed({ timestamp: release["created_at"] });
                    embed.setAuthor(release["tag_name"] + ": " + release["name"], undefined, release["html_url"]);
                    embed.setDescription(release["body"]);
                    embed.setFooter(type);
                    embed.setColor("#2691b3");
                    global.bot.channels.find("name", "darkbot_project").sendEmbed(embed, "Nueva version!");
                }
            });
        });
        req.on("error", (err) => { logger.error('Request Error:' + err.message) });
        req.end();

        //Eliminar version antigua
        try {
            child_process.execSync("rm -rf ../" + global.config.oldVersion);
        } catch (err) {
            console.log("Error al eliminar version antigua: " + err.message);
        }

        global.config.oldVersion = undefined; //Evitar que vuelva a anunciar la nueva version.
    }
    global.bot.user.setGame("version " + global.config.version + " ❤");
});
<commit_msg>Fixed #36<commit_after>const fs = require("fs");
const http = require("http");
const https = require("https");
const Discord = require("discord.js");
const child_process = require("child_process");
const createHandler = require("github-webhook-handler");
const handler = createHandler({path: global.config.webhook.path, secret: global.config.webhook.secret});

//El servidor que se encarga de recibir las POST requests de github (webhooks)
const server = http.createServer((req, res) => {
    let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress ||
            req.socket.remoteAddress || req.connection.socket.remoteAddress;
    console.log(`[${new Date().toLocaleString()}] Incoming request: \n\tClient IP: ${ip}\n\tPath: ${req.url}\n\tMethod: ${req.method}`);
    handler(req, res, (err) => {
        res.statusCode = 404;
        res.end("No such file or directory.");
    });
});

//Evita arrancar el servidor http en caso de que travis tenga el control
if(global.config.version !== 'travis')
    server.listen(global.config.webhook.port);

//Evento se ejecuta cuando sale una release del bot
handler.on("release", (evt) => {
    let newVersion = evt.payload.release.tag_name;
    console.log("UPDATING!");
    //Asegurarnos de que el bot esta `ready`
    if(global.bot.readyAt) {
        global.bot.user.setGame("Actualizando a " + newVersion);
    }
    //cerrar el servidor http para que el nuevo bot pueda escuchar el mismo puerto.
    server.close();

    //Clonar la nueva version a una carpeta paralela
    child_process.execSync("git clone -b master https:\/\/github.com/darkaqua/darkbot ../" + newVersion);
    process.chdir("../" + newVersion)
    //Instalar dependencias
    child_process.execSync("npm install");

    //Abrir archivo para el log
    const stdio = fs.openSync("out.log", "a");

    //Destruir el bot actual y ejecutar el nuevo.
    child_process.spawn("node", ["main.js", newVersion, global.config.version], { detached: true, stdio: ["ignore", stdio, stdio] });
    process.exit();
});

handler.on("error", (err) => {
    console.log("Webhook Handler Error: " + err.message);
});

global.bot.on("ready", () => {
    if(global.config.oldVersion) {
        //Avisar con embed de que se ha actualizado.
        //Pide info sobre la nueva version a la api de github.
        let options = {
            hostname: "api.github.com",
            path: `/repos/darkaqua/darkbot/releases/tags/${global.config.version}`,
            headers: { "User-Agent": "darkaqua-darkbot" }
        };
        let req = https.request(options, (res) => {
            let data = "";
            res.on("data", chunk => { data += chunk; });
            res.on("end", () => {
                if(res.statusCode !== 404) {
                    let release = JSON.parse(data);
                    let type = release["prerelease"] ? "pre-release" : "release";
                    let embed = new Discord.RichEmbed({ timestamp: release["created_at"] });
                    embed.setAuthor(release["tag_name"] + ": " + release["name"], undefined, release["html_url"]);
                    embed.setDescription(release["body"]);
                    embed.setFooter(type);
                    embed.setColor("#2691b3");
                    global.bot.channels.find("name", "darkbot_project").sendEmbed(embed, "Nueva version!");
                }
            });
        });
        req.on("error", (err) => { logger.error('Request Error:' + err.message) });
        req.end();

        //Eliminar version antigua
        try {
            child_process.execSync("rm -rf ../" + global.config.oldVersion);
        } catch (err) {
            console.log("Error al eliminar version antigua: " + err.message);
        }

        global.config.oldVersion = undefined; //Evitar que vuelva a anunciar la nueva version.
    }
    global.bot.user.setGame("version " + global.config.version + " ❤");
});
<|endoftext|>


<commit_before>/**
 * ==  scripty2 fx ==
 * The scripty2 effects framework provides for time-based transformations of DOM elements
 * and arbitrary JavaScript objects. This is at the core of scripty2 and presents a refinement
 * of the visual effects framework of script.aculo.us 1.X.
 *
 * In practice [[s2.fx.Morph]] is most often used, which allows transitions from one
 * set of CSS style rules to another.
 *
 *
Introduction 0 ? 'start' : 'stop']();
  }

  function renderQueues(){
    queues.invoke('render', heartbeat.getTimestamp());
  }

  function initialize(initialHeartbeat){
    if(globalQueue) return;
    queues.push(globalQueue = new s2.fx.Queue());
    s2.fx.DefaultOptions.queue = globalQueue;
    heartbeat = initialHeartbeat || new s2.fx.Heartbeat();

    document
      .observe('effect:heartbeat', renderQueues)
      .observe('effect:queued',    beatOnDemand.curry(1))
      .observe('effect:dequeued',  beatOnDemand.curry(-1));
  }

  return {
    initialize: initialize,
    getQueues: function(){ return queues; },
    addQueue: queues.push,
    getHeartbeat: function(){ return heartbeat; },
    setHeartbeat: function(newHeartbeat){
      heartbeat = newHeartbeat;
    }
  }
})();

Object.extend(s2.fx, {
  DefaultOptions: {
    transition: 'sinusoidal',
    position:   'parallel',
    fps:        60,
    duration:   .2
  },

  elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },

  parseOptions: function(options) {
    if (Object.isNumber(options))
      options = { duration: options };
    else if (Object.isFunction(options))
      options = { after: options };
    else if (Object.isString(options))
      options = { duration: options == 'slow' ? 1 : options == 'fast' ? .1 : .2 };

    return options;
  }
});

/**
 *  class s2.fx.Base
**/
s2.fx.Base = Class.create({
  /**
   *  new s2.fx.Base([options])
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Base calls for all effects. Subclasses need to define the `update` instance
   *  method for it to be useful. scripty2 currently provides one subclass implementation
   *  for effects based on DOM elements, [[s2.fx.Element]].
   *
   *
Effect options

   *
   *  There are serveral ways the options argument can be used:
   *
   *      new s2.fx.Base({ duration: 3, transition: 'spring' });
   *      new s2.fx.Base(function(){})   // shortcut for { after: function(){} }
   *      new s2.fx.Base(3);             // shortcut for { duration: 3 }
   *      new s2.fx.Base('slow');        // shortcut for { duration: 1 }
   *      new s2.fx.Base('fast');        // shortcut for { duration: .1 }
   *
   *  The following options are recognized:
   *
   *  * `duration`: duration in seconds, defaults to 0.2 (a fifth of a second). This
   *     speed is based on the value Mac OS X uses for interface effects.
   *  * `transition`: Function reference or String with a property name from [[s2.fx.Transitions]].
   *    Sets the transition method for easing and other special effects.
   *  * `before`: Function to be executed before the first frame of the effect is rendered.
   *  * `after`: Function to be executed after the effect has finished.
   *  * `beforeUpdate`: Function to be executed before each frame is rendered.
   *  * `afterUpdate`: Function to be executed after each frame is renedred.
   *  * `fps`: The maximum number of frames per second. Ensures that no more than `options.fps`
   *    frames per second are rendered, even if there's enough computation resources available.
   *    This can be used to make CPU-intensive effects use fewer resources.
   *  * `queue`: Specify a [[s2.fx.Queue]] to be used for the effect.
   *
   *  The effect won't start immediately, it will merely be initialized.
   *  To start the effect, call [[s2.fx.Base#play]].
  **/
  initialize: function(options) {
    s2.fx.initialize();
    this.updateWithoutWrappers = this.update;

    if(options && options.queue && !s2.fx.getQueues().include(options.queue))
      s2.fx.addQueue(options.queue);

    this.setOptions(options);
    this.duration = this.options.duration*1000;
    this.state = 'idle';

    ['after','before'].each(function(method) {
      this[method] = function(method) {
        method(this);
        return this;
      }
    }, this);
  },

  setOptions: function(options) {
    options = s2.fx.parseOptions(options);

    if (!this.options) {
      this.options = Object.extend(Object.extend({},s2.fx.DefaultOptions), options);
      if(options.tween) this.options.transition = options.tween;
    }

    if (this.options.beforeUpdate || this.options.afterUpdate) {
      this.update = this.updateWithoutWrappers.wrap( function(proceed,position){
        if (this.options.beforeUpdate) this.options.beforeUpdate(this);
        proceed(position);
        if (this.options.afterUpdate) this.options.afterUpdate(this);
      }.bind(this));
    }
    if(this.options.transition === false)
      this.options.transition = s2.fx.Transitions.linear;
    this.options.transition = Object.propertize(this.options.transition, s2.fx.Transitions);
  },

  /**
   *  s2.fx.Base#play([options]) -> s2.fx.Base
   *  - options: Effect options, see above.
   *
   *  Starts playing the effect.
   *  Returns the effect.
  **/
  play: function(options) {
    this.setOptions(options);
    this.frameCount = 0;
    this.options.queue.add(this);
    this.maxFrames = this.options.fps * this.duration / 1000;
    return this;
  },

  /**
   *  s2.fx.Base#render(timestamp) -> undefined
   *  - timestamp (Date): point in time, normally the current time
   *
   *  Renders the effect, and calls the before/after functions when necessary.
   *  This method also switches the state of the effect from `idle` to `running` when
   *  the first frame is rendered, and from `running` to `finished` after the last frame
   *  is rendered.
  **/
  render: function(timestamp) {
    if (timestamp >= this.startsAt) {
      if (this.state == 'idle') {
        if (this.options.before) this.options.before(this);
        if (this.setup) this.setup();
        this.state = 'running';
        this.update(this.options.transition(0));
        this.frameCount++;
        return this;
      }
      if (timestamp >= this.endsAt && !(this.state == 'finished')) {
        this.update(this.options.transition(1));
        if (this.teardown) this.teardown();
        if (this.options.after) this.options.after(this);
        this.state = 'finished';
      } else if (this.state == 'running') {
        var position = 1 - (this.endsAt - timestamp) / this.duration;
        if ((this.maxFrames * position).floor() > this.frameCount) {
          this.update(this.options.transition(position));
          this.frameCount++;
        }
      }
    }
    return this;
  },

  /**
   *  s2.fx.Base#cancel([after]) -> undefined
   *  - after (Boolean): if true, run the after method (if defined), defaults to false
   *
   *  Calling `cancel()` immediately halts execution of the effect, and calls the `teardown`
   *  method if defined.
  **/
  cancel: function(after) {
    if(!this.state == 'running') return;
    if (this.teardown) this.teardown();
    if (after && this.options.after) this.options.after(this);
    this.state = 'finished';
  },

  /**
   *  s2.fx.Base#inspect() -> String
   *
   *  Returns the debug-oriented string representation of an effect.
  **/
  inspect: function() {
    return '#<s2.fx:' + [this.state, this.startsAt, this.endsAt].inspect() + '>';
  }
});

/**
 * class s2.fx.Element < s2.fx.Base
 * Base class for effects that change DOM elements. This is the base class for
 * the most important effects implementation [[s2.fx.Morph]], but can be used
 * as a base class for non-CSS based effects too.
**/
s2.fx.Element = Class.create(s2.fx.Base, {
  /**
   *  new s2.fx.Element(element[, options])
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  See [[s2.fx.Base]] for a description of the `options` argument.
  **/
  initialize: function($super, element, options) {
    if(!(this.element = $(element)))
      throw(s2.fx.elementDoesNotExistError);
    this.operators = [];
    return $super(options);
  },

  /**
   *  s2.fx.Element#animate(operator[, args...]) -> undefined
   *  - operator (String): lowercase name of an [[s2.fx.Operator]]
   *
   *  Starts an animation by using a [[s2.fx.Operator]] on the element
   *  that is associated with the effect.
   *
   *  The rest of the arguments are passed to Operators' constructor.
   *  This method is intended to be called in the `setup` instance method
   *  of subclasses, for example:
   *
   *      // setup method from s2.fx.Style
   *      setup: function() {
   *        this.animate('style', this.element, { style: this.options.style });
   *      }
  **/
  animate: function() {
    var args = $A(arguments), operator =  args.shift();
    operator = operator.charAt(0).toUpperCase() + operator.substring(1);
    this.operators.push(new s2.fx.Operators[operator](this, args[0], args[1] || {}));
  },

  /**
   *  s2.fx.Element#play([element[, options]]) -> s2.fx.Base
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Starts playing the element effect. The optional `element` argument can
   *  be used to reuse this instance on a different element than for
   *  which it was used originally.
  **/
  play: function($super, element, options) {
    if (element) this.element = $(element);
    return $super(options);
  },

  update: function(position) {
    this.operators.invoke('render', position);
  }
});
<commit_msg>s2.fx.Base#before/afterUpdate now receive the position as second parameter<commit_after>/**
 * ==  scripty2 fx ==
 * The scripty2 effects framework provides for time-based transformations of DOM elements
 * and arbitrary JavaScript objects. This is at the core of scripty2 and presents a refinement
 * of the visual effects framework of script.aculo.us 1.X.
 *
 * In practice [[s2.fx.Morph]] is most often used, which allows transitions from one
 * set of CSS style rules to another.
 *
 *
Introduction 0 ? 'start' : 'stop']();
  }

  function renderQueues(){
    queues.invoke('render', heartbeat.getTimestamp());
  }

  function initialize(initialHeartbeat){
    if(globalQueue) return;
    queues.push(globalQueue = new s2.fx.Queue());
    s2.fx.DefaultOptions.queue = globalQueue;
    heartbeat = initialHeartbeat || new s2.fx.Heartbeat();

    document
      .observe('effect:heartbeat', renderQueues)
      .observe('effect:queued',    beatOnDemand.curry(1))
      .observe('effect:dequeued',  beatOnDemand.curry(-1));
  }

  return {
    initialize: initialize,
    getQueues: function(){ return queues; },
    addQueue: queues.push,
    getHeartbeat: function(){ return heartbeat; },
    setHeartbeat: function(newHeartbeat){
      heartbeat = newHeartbeat;
    }
  }
})();

Object.extend(s2.fx, {
  DefaultOptions: {
    transition: 'sinusoidal',
    position:   'parallel',
    fps:        60,
    duration:   .2
  },

  elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },

  parseOptions: function(options) {
    if (Object.isNumber(options))
      options = { duration: options };
    else if (Object.isFunction(options))
      options = { after: options };
    else if (Object.isString(options))
      options = { duration: options == 'slow' ? 1 : options == 'fast' ? .1 : .2 };

    return options;
  }
});

/**
 *  class s2.fx.Base
**/
s2.fx.Base = Class.create({
  /**
   *  new s2.fx.Base([options])
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Base calls for all effects. Subclasses need to define the `update` instance
   *  method for it to be useful. scripty2 currently provides one subclass implementation
   *  for effects based on DOM elements, [[s2.fx.Element]].
   *
   *
Effect options

   *
   *  There are serveral ways the options argument can be used:
   *
   *      new s2.fx.Base({ duration: 3, transition: 'spring' });
   *      new s2.fx.Base(function(){})   // shortcut for { after: function(){} }
   *      new s2.fx.Base(3);             // shortcut for { duration: 3 }
   *      new s2.fx.Base('slow');        // shortcut for { duration: 1 }
   *      new s2.fx.Base('fast');        // shortcut for { duration: .1 }
   *
   *  The following options are recognized:
   *
   *  * `duration`: duration in seconds, defaults to 0.2 (a fifth of a second). This
   *     speed is based on the value Mac OS X uses for interface effects.
   *  * `transition`: Function reference or String with a property name from [[s2.fx.Transitions]].
   *    Sets the transition method for easing and other special effects.
   *  * `before`: Function to be executed before the first frame of the effect is rendered.
   *  * `after`: Function to be executed after the effect has finished.
   *  * `beforeUpdate`: Function to be executed before each frame is rendered. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `afterUpdate`: Function to be executed after each frame is renedred. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `fps`: The maximum number of frames per second. Ensures that no more than `options.fps`
   *    frames per second are rendered, even if there's enough computation resources available.
   *    This can be used to make CPU-intensive effects use fewer resources.
   *  * `queue`: Specify a [[s2.fx.Queue]] to be used for the effect.
   *
   *  The effect won't start immediately, it will merely be initialized.
   *  To start the effect, call [[s2.fx.Base#play]].
  **/
  initialize: function(options) {
    s2.fx.initialize();
    this.updateWithoutWrappers = this.update;

    if(options && options.queue && !s2.fx.getQueues().include(options.queue))
      s2.fx.addQueue(options.queue);

    this.setOptions(options);
    this.duration = this.options.duration*1000;
    this.state = 'idle';

    ['after','before'].each(function(method) {
      this[method] = function(method) {
        method(this);
        return this;
      }
    }, this);
  },

  setOptions: function(options) {
    options = s2.fx.parseOptions(options);

    if (!this.options) {
      this.options = Object.extend(Object.extend({},s2.fx.DefaultOptions), options);
      if(options.tween) this.options.transition = options.tween;
    }

    if (this.options.beforeUpdate || this.options.afterUpdate) {
      this.update = this.updateWithoutWrappers.wrap( function(proceed,position){
        if (this.options.beforeUpdate) this.options.beforeUpdate(this, position);
        proceed(position);
        if (this.options.afterUpdate) this.options.afterUpdate(this, position);
      }.bind(this));
    }
    if(this.options.transition === false)
      this.options.transition = s2.fx.Transitions.linear;
    this.options.transition = Object.propertize(this.options.transition, s2.fx.Transitions);
  },

  /**
   *  s2.fx.Base#play([options]) -> s2.fx.Base
   *  - options: Effect options, see above.
   *
   *  Starts playing the effect.
   *  Returns the effect.
  **/
  play: function(options) {
    this.setOptions(options);
    this.frameCount = 0;
    this.options.queue.add(this);
    this.maxFrames = this.options.fps * this.duration / 1000;
    return this;
  },

  /**
   *  s2.fx.Base#render(timestamp) -> undefined
   *  - timestamp (Date): point in time, normally the current time
   *
   *  Renders the effect, and calls the before/after functions when necessary.
   *  This method also switches the state of the effect from `idle` to `running` when
   *  the first frame is rendered, and from `running` to `finished` after the last frame
   *  is rendered.
  **/
  render: function(timestamp) {
    if (timestamp >= this.startsAt) {
      if (this.state == 'idle') {
        if (this.options.before) this.options.before(this);
        if (this.setup) this.setup();
        this.state = 'running';
        this.update(this.options.transition(0));
        this.frameCount++;
        return this;
      }
      if (timestamp >= this.endsAt && !(this.state == 'finished')) {
        this.update(this.options.transition(1));
        if (this.teardown) this.teardown();
        if (this.options.after) this.options.after(this);
        this.state = 'finished';
      } else if (this.state == 'running') {
        var position = 1 - (this.endsAt - timestamp) / this.duration;
        if ((this.maxFrames * position).floor() > this.frameCount) {
          this.update(this.options.transition(position));
          this.frameCount++;
        }
      }
    }
    return this;
  },

  /**
   *  s2.fx.Base#cancel([after]) -> undefined
   *  - after (Boolean): if true, run the after method (if defined), defaults to false
   *
   *  Calling `cancel()` immediately halts execution of the effect, and calls the `teardown`
   *  method if defined.
  **/
  cancel: function(after) {
    if(!this.state == 'running') return;
    if (this.teardown) this.teardown();
    if (after && this.options.after) this.options.after(this);
    this.state = 'finished';
  },

  /**
   *  s2.fx.Base#inspect() -> String
   *
   *  Returns the debug-oriented string representation of an effect.
  **/
  inspect: function() {
    return '#<s2.fx:' + [this.state, this.startsAt, this.endsAt].inspect() + '>';
  }
});

/**
 * class s2.fx.Element < s2.fx.Base
 * Base class for effects that change DOM elements. This is the base class for
 * the most important effects implementation [[s2.fx.Morph]], but can be used
 * as a base class for non-CSS based effects too.
**/
s2.fx.Element = Class.create(s2.fx.Base, {
  /**
   *  new s2.fx.Element(element[, options])
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  See [[s2.fx.Base]] for a description of the `options` argument.
  **/
  initialize: function($super, element, options) {
    if(!(this.element = $(element)))
      throw(s2.fx.elementDoesNotExistError);
    this.operators = [];
    return $super(options);
  },

  /**
   *  s2.fx.Element#animate(operator[, args...]) -> undefined
   *  - operator (String): lowercase name of an [[s2.fx.Operator]]
   *
   *  Starts an animation by using a [[s2.fx.Operator]] on the element
   *  that is associated with the effect.
   *
   *  The rest of the arguments are passed to Operators' constructor.
   *  This method is intended to be called in the `setup` instance method
   *  of subclasses, for example:
   *
   *      // setup method from s2.fx.Style
   *      setup: function() {
   *        this.animate('style', this.element, { style: this.options.style });
   *      }
  **/
  animate: function() {
    var args = $A(arguments), operator =  args.shift();
    operator = operator.charAt(0).toUpperCase() + operator.substring(1);
    this.operators.push(new s2.fx.Operators[operator](this, args[0], args[1] || {}));
  },

  /**
   *  s2.fx.Element#play([element[, options]]) -> s2.fx.Base
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Starts playing the element effect. The optional `element` argument can
   *  be used to reuse this instance on a different element than for
   *  which it was used originally.
  **/
  play: function($super, element, options) {
    if (element) this.element = $(element);
    return $super(options);
  },

  update: function(position) {
    this.operators.invoke('render', position);
  }
});
<|endoftext|>


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

    define(
        [
            'lodash',
            'knockout',
            'querystring',
            'config/routes',
            'util/container',
            'models/DynamicRecord',
            'models/ListPager',
            'ui/pages/search/SearchType'
        ],
        function (_, ko, qs, routes, container, DynamicRecord, ListPager, SearchType) {
            var RESULTS_MODES = [ 'List', 'Thumbnails', 'Table' ];

            return function (context) {
                var settings = container.resolve('settings.' + context.params.type),
                    selectedSearchType = ko.observable(),
                    submittedQuery = ko.observable(),
                    results = ko.observableArray(),
                    query = qs.parse(window.location.search.replace(/^\?/, '')),
                    doSearch = function (callback) {
                        submittedQuery(this.searchText());
                        results([]);
                        this.loading(true);
                        this.displayResults(false);
                        container.resolve('search.' + context.params.type)(
                            _.zipObject(
                                [ selectedSearchType() ],
                                [ submittedQuery() ]
                            ),
                            function (err, searchServiceResults) {
                                this.loading(false);
                                if (err) {
                                    // TODO Display error
                                    return;
                                }
                                results(_.map(searchServiceResults, function (result) {
                                    return new DynamicRecord(result, this.searchResultFields);
                                }.bind(this)));
                                this.displayResults(true);
                                if (_.isFunction(callback)) {
                                    callback();
                                }
                            }.bind(this)
                        );
                    }.bind(this);

                this.pager = new ListPager(
                    results,
                    query.start ? parseInt(query.start, 10) : undefined,
                    query.size ? parseInt(query.size, 10) : undefined
                );

                this.searchText = ko.observable(query.query || '');
                this.searchTypes = ko.observableArray();
                this.searchResultFields = ko.observableArray();
                this.loading = ko.observable(false);
                this.displayResults = ko.observable(false);
                this.resultsMode = ko.observable(query.mode || 'List');

                this.type = ko.pureComputed(function () {
                    return context.params.type;
                });

                this.submittedQuery = ko.pureComputed(function () {
                    return submittedQuery();
                });

                this.heading = ko.pureComputed(function () {
                    return settings.collectionName + ' Search';
                });

                this.placeholder = ko.pureComputed(function () {
                    return selectedSearchType() ?
                        _.find(this.searchTypes(), { key: selectedSearchType() }).placeholder :
                        'Enter your search terms...';
                }.bind(this));

                this.hasResults = ko.pureComputed(function () {
                    return results().length > 0;
                });

                this.availableResultsModes = ko.pureComputed(function () {
                    return RESULTS_MODES;
                });

                this.resultsModeClassName = ko.pureComputed(function () {
                    return this.resultsMode().toLowerCase();
                }.bind(this));

                this.binding = function (element, callback) {
                    require(
                        [
                            settings.searchTypes,
                            settings.searchResultFields
                        ],
                        function (searchTypes, searchResultFields) {
                            this.searchTypes(_.map(searchTypes, function (type) {
                                return new SearchType(type, selectedSearchType);
                            }));
                            this.searchTypes()[0].makeActive();
                            this.searchResultFields(searchResultFields);
                            if (this.searchText()) {
                                doSearch(callback);
                            } else {
                                callback();
                            }
                        }.bind(this)
                    );
                };

                this.reset = function () {
                    results([]);
                    this.displayResults(false);
                    this.searchText('');
                    this.searchTypes()[0].makeActive();
                };

                this.submit = function (callback) {
                    if (!this.searchText()) {
                        return;
                    }
                    this.pager.start(0);
                    window.history.pushState({}, window.title, this.searchUrlFor({ query: this.searchText() }));
                    doSearch(callback);
                    return false;
                };

                this.displayFor = function (field, result) {
                    if (_.isString(field)) {
                        field = _.find(this.searchResultFields(), { key: field });
                    }
                    return {
                        name: 'display/' + (field.display || 'text'),
                        params: {
                            data: result.data,
                            name: field.key,
                            placeholder: field.placeholder
                        }
                    };
                };

                this.searchUrlFor = function (overrides) {
                    return routes.searchUrlFor(
                        context.params.type,
                        _.defaults(
                            overrides,
                            {
                                query: submittedQuery(),
                                start: this.pager.start(),
                                size: this.pager.pageSize(),
                                mode: this.resultsMode()
                            }
                        )
                    );
                };

                this.detailUrlFor = function (result) {
                    return routes.detailUrlFor(context.params.type, result.id());
                };
            };
        }
    );
}());
<commit_msg>Fix class name generator.<commit_after>(function () {
    'use strict';

    define(
        [
            'lodash',
            'knockout',
            'querystring',
            'config/routes',
            'util/container',
            'models/DynamicRecord',
            'models/ListPager',
            'ui/pages/search/SearchType'
        ],
        function (_, ko, qs, routes, container, DynamicRecord, ListPager, SearchType) {
            var RESULTS_MODES = [ 'List', 'Thumbnails', 'Table' ];

            return function (context) {
                var settings = container.resolve('settings.' + context.params.type),
                    selectedSearchType = ko.observable(),
                    submittedQuery = ko.observable(),
                    results = ko.observableArray(),
                    query = qs.parse(window.location.search.replace(/^\?/, '')),
                    doSearch = function (callback) {
                        submittedQuery(this.searchText());
                        results([]);
                        this.loading(true);
                        this.displayResults(false);
                        container.resolve('search.' + context.params.type)(
                            _.zipObject(
                                [ selectedSearchType() ],
                                [ submittedQuery() ]
                            ),
                            function (err, searchServiceResults) {
                                this.loading(false);
                                if (err) {
                                    // TODO Display error
                                    return;
                                }
                                results(_.map(searchServiceResults, function (result) {
                                    return new DynamicRecord(result, this.searchResultFields);
                                }.bind(this)));
                                this.displayResults(true);
                                if (_.isFunction(callback)) {
                                    callback();
                                }
                            }.bind(this)
                        );
                    }.bind(this);

                this.pager = new ListPager(
                    results,
                    query.start ? parseInt(query.start, 10) : undefined,
                    query.size ? parseInt(query.size, 10) : undefined
                );

                this.searchText = ko.observable(query.query || '');
                this.searchTypes = ko.observableArray();
                this.searchResultFields = ko.observableArray();
                this.loading = ko.observable(false);
                this.displayResults = ko.observable(false);
                this.resultsMode = ko.observable(query.mode || 'List');

                this.type = ko.pureComputed(function () {
                    return context.params.type;
                });

                this.submittedQuery = ko.pureComputed(function () {
                    return submittedQuery();
                });

                this.heading = ko.pureComputed(function () {
                    return settings.collectionName + ' Search';
                });

                this.placeholder = ko.pureComputed(function () {
                    return selectedSearchType() ?
                        _.find(this.searchTypes(), { key: selectedSearchType() }).placeholder :
                        'Enter your search terms...';
                }.bind(this));

                this.hasResults = ko.pureComputed(function () {
                    return results().length > 0;
                });

                this.availableResultsModes = ko.pureComputed(function () {
                    return RESULTS_MODES;
                });

                this.resultsModeContainerClassName = ko.pureComputed(function () {
                    return 'results-container-' + this.resultsMode().toLowerCase();
                }.bind(this));

                this.binding = function (element, callback) {
                    require(
                        [
                            settings.searchTypes,
                            settings.searchResultFields
                        ],
                        function (searchTypes, searchResultFields) {
                            this.searchTypes(_.map(searchTypes, function (type) {
                                return new SearchType(type, selectedSearchType);
                            }));
                            this.searchTypes()[0].makeActive();
                            this.searchResultFields(searchResultFields);
                            if (this.searchText()) {
                                doSearch(callback);
                            } else {
                                callback();
                            }
                        }.bind(this)
                    );
                };

                this.reset = function () {
                    results([]);
                    this.displayResults(false);
                    this.searchText('');
                    this.searchTypes()[0].makeActive();
                };

                this.submit = function (callback) {
                    if (!this.searchText()) {
                        return;
                    }
                    this.pager.start(0);
                    window.history.pushState({}, window.title, this.searchUrlFor({ query: this.searchText() }));
                    doSearch(callback);
                    return false;
                };

                this.displayFor = function (field, result) {
                    if (_.isString(field)) {
                        field = _.find(this.searchResultFields(), { key: field });
                    }
                    return {
                        name: 'display/' + (field.display || 'text'),
                        params: {
                            data: result.data,
                            name: field.key,
                            placeholder: field.placeholder
                        }
                    };
                };

                this.searchUrlFor = function (overrides) {
                    return routes.searchUrlFor(
                        context.params.type,
                        _.defaults(
                            overrides,
                            {
                                query: submittedQuery(),
                                start: this.pager.start(),
                                size: this.pager.pageSize(),
                                mode: this.resultsMode()
                            }
                        )
                    );
                };

                this.detailUrlFor = function (result) {
                    return routes.detailUrlFor(context.params.type, result.id());
                };
            };
        }
    );
}());
<|endoftext|>


<commit_before>var makerjs = require('../target/js/node.maker.js');

function tubeClamp(tubediameter, thickness, distance, open, wing, lid, lidclearance) {

    this.paths = {};
    this.models = {};

    var line = makerjs.paths.Line;
    var arc = makerjs.paths.Arc;
    var point = makerjs.point;

    var radius = tubediameter / 2;
    var d2 = distance / 2;
    var t2 = thickness / 2;
    var cy = distance + radius;
    var outer = radius + wing;
    var mtop = distance + tubediameter - open;
    var drop = 0;//radius / 4;

    var z = Math.max(thickness + .125 - radius, 0);
    var bottom = Math.max(radius, thickness * 1.2);

    //this.paths.push(new makerjs.paths.Circle('tube', [0, cy], radius));

    var thicknessAngle = 360 - makerjs.angle.toDegrees(Math.acos(t2 / radius));
    var arc1 = new arc([0, cy], radius, thicknessAngle, 0);
    var arc1Points = point.fromArc(arc1);

    var halfBody = {
        models: {
            scurve: makerjs.model.move(new makerjs.models.SCurve(wing - (bottom - radius), cy - drop), [bottom, 0])
        },
        paths: {
            bottom: new line([0, 0], [bottom, 0]),
            //new line('longslope', [radius, 0], [outer, cy - drop]),
            crux: new line([outer, cy - drop], [outer, mtop]),
            flat: new line([outer, mtop], [radius, mtop]),
            wall: new line([radius, mtop], [radius, cy]),
            arc: arc1,
            boxside: new line(arc1Points[0], [t2, d2]),
            boxottom: new line([0, d2], [t2, d2])
        }
    };

    var lidAngle = makerjs.angle.toDegrees(Math.acos((radius - lidclearance) / radius));
    var arc2 = new arc([0, -radius], radius, lidAngle, 90);
    var arc2Points = point.fromArc(arc2);

    var halfLid = new makerjs.models.ConnectTheDots(false, [arc2Points[0], [arc2Points[0][0], 0], [outer, 0], [outer, lid], [0, lid]]);
    halfLid.paths['lid'] = arc2;

    var lid = {
        id: 'lid',
        //                origin: [ 0, cy + radius - arc2Points[0].y - open ],
        origin: [0, cy + radius],
        models: {
            halflid: halfLid,
            halfLidMirror: makerjs.model.mirror(halfLid, true, false)
        }
    };

    var body = {
        models: {'halfBody': halfBody, 'mirror': makerjs.model.mirror(halfBody, true, false)}
    };

    this.models.body = body;
    this.models.lid = lid;

    this.units = makerjs.unitType.Inch;
    this.origin = [0, -cy];
};

tubeClamp.metaParameters = [
    { title: "tubediameter", type: "range", min: .5, max: 3, step: .0625, value: 0.875 },
    { title: "thickness", type: "range", min: .125, max: 1, step: .0625, value: .5 },
    { title: "distance", type: "range", min: .25, max: 2, step: .125, value: 1 },
    { title: "open", type: "range", min: 0, max: .25, step: .0625, value: .0625 },
    { title: "wing", type: "range", min: .25, max: 1.5, step: .0625, value: .5 },
    { title: "lid", type: "range", min: .125, max: .75, step: .0625, value: .25 },
    { title: "lidclearance", type: "range", min: 0, max: .13, step: .01, value: .01 }
];

module.exports = tubeClamp;
<commit_msg>added dogbone<commit_after>var makerjs = require('../target/js/node.maker.js');

function tubeClamp(tubediameter, thickness, distance, open, wing, lid, lidclearance) {

    this.paths = {};
    this.models = {};

    var line = makerjs.paths.Line;
    var arc = makerjs.paths.Arc;
    var point = makerjs.point;

    var radius = tubediameter / 2;
    var d2 = distance / 2;
    var t2 = thickness / 2;
    var cy = distance + radius;
    var outer = radius + wing;
    var mtop = distance + tubediameter - open;
    var drop = 0;//radius / 4;

    var z = Math.max(thickness + .125 - radius, 0);
    var bottom = Math.max(radius, thickness * 1.2);

    //this.paths.push(new makerjs.paths.Circle('tube', [0, cy], radius));

    var thicknessAngle = 360 - makerjs.angle.toDegrees(Math.acos(t2 / radius));
    var arc1 = new arc([0, cy], radius, thicknessAngle, 0);
    var arc1Points = point.fromArc(arc1);

    var halfBody = {
        models: {
            scurve: makerjs.model.move(new makerjs.models.SCurve(wing - (bottom - radius), cy - drop), [bottom, 0])
        },
        paths: {
            bottom: new line([0, 0], [bottom, 0]),
            //new line('longslope', [radius, 0], [outer, cy - drop]),
            crux: new line([outer, cy - drop], [outer, mtop]),
            flat: new line([outer, mtop], [radius, mtop]),
            wall: new line([radius, mtop], [radius, cy]),
            arc: arc1,
            boxside: new line(arc1Points[0], [t2, d2]),
            boxottom: new line([0, d2], [t2, d2])
        }
    };

    halfBody.paths.dogbone = makerjs.path.dogbone(halfBody.paths.boxottom, halfBody.paths.boxside, .03);

    var lidAngle = makerjs.angle.toDegrees(Math.acos((radius - lidclearance) / radius));
    var arc2 = new arc([0, -radius], radius, lidAngle, 90);
    var arc2Points = point.fromArc(arc2);

    var halfLid = new makerjs.models.ConnectTheDots(false, [arc2Points[0], [arc2Points[0][0], 0], [outer, 0], [outer, lid], [0, lid]]);
    halfLid.paths['lid'] = arc2;

    var lid = {
        id: 'lid',
        //                origin: [ 0, cy + radius - arc2Points[0].y - open ],
        origin: [0, cy + radius],
        models: {
            halflid: halfLid,
            halfLidMirror: makerjs.model.mirror(halfLid, true, false)
        }
    };

    var body = {
        models: {'halfBody': halfBody, 'mirror': makerjs.model.mirror(halfBody, true, false)}
    };

    this.models.body = body;
    this.models.lid = lid;

    this.units = makerjs.unitType.Inch;
    this.origin = [0, -cy];
};

tubeClamp.metaParameters = [
    { title: "tubediameter", type: "range", min: .5, max: 3, step: .0625, value: 0.875 },
    { title: "thickness", type: "range", min: .125, max: 1, step: .0625, value: .5 },
    { title: "distance", type: "range", min: .25, max: 2, step: .125, value: 1 },
    { title: "open", type: "range", min: 0, max: .25, step: .0625, value: .0625 },
    { title: "wing", type: "range", min: .25, max: 1.5, step: .0625, value: .5 },
    { title: "lid", type: "range", min: .125, max: .75, step: .0625, value: .25 },
    { title: "lidclearance", type: "range", min: 0, max: .13, step: .01, value: .01 }
];

module.exports = tubeClamp;
<|endoftext|>


<commit_before>import { collectStyles, addHeader } from './functions'
import Print from './print'

export default {
  print: (params, printFrame) => {
    // Get the DOM printable element
    const printElement = isHtmlElement(params.printable) ? params.printable : document.getElementById(params.printable)

    // Check if the element exists
    if (!printElement) {
      window.console.error('Invalid HTML element id: ' + params.printable)
      return
    }

    // Clone the target element including its children (if available)
    params.printableElement = cloneElement(printElement, params)

    // Add header
    if (params.header) {
      addHeader(params.printableElement, params)
    }

    // Print html element contents
    Print.send(params, printFrame)
  }
}

function cloneElement (element, params) {
  // Clone the main node (if not already inside the recursion process)
  const clone = element.cloneNode()

  // Loop over and process the children elements / nodes (including text nodes)
  const childNodesArray = Array.prototype.slice.call(element.childNodes)
  for (let i = 0; i < childNodesArray.length; i++) {
    // Check if we are skiping the current element
    if (params.ignoreElements.indexOf(childNodesArray[i].id) !== -1) {
      continue
    }

    // Clone the child element
    const clonedChild = cloneElement(childNodesArray[i], params)

    // Attach the cloned child to the cloned parent node
    clone.appendChild(clonedChild)
  }

  // Get all styling for print element (for nodes of type element only)
  if (params.scanStyles && element.nodeType === 1) {
    clone.setAttribute('style', collectStyles(element, params))
  }

  // Check if the element needs any state processing (copy user input data)
  switch (element.tagName) {
    case 'SELECT':
      // Copy the current selection value to its clone
      clone.value = element.value
      break
    case 'CANVAS':
      // Copy the canvas content to its clone
      clone.getContext('2d').drawImage(element, 0, 0)
      break
  }

  return clone
}

function isHtmlElement (printable) {
  // Check if element is instance of HTMLElement or has nodeType === 1 (for elements in iframe)
  return typeof printable === 'object' && printable && (printable instanceof HTMLElement || printable.nodeType === 1)
}
<commit_msg>Fix comment typo<commit_after>import { collectStyles, addHeader } from './functions'
import Print from './print'

export default {
  print: (params, printFrame) => {
    // Get the DOM printable element
    const printElement = isHtmlElement(params.printable) ? params.printable : document.getElementById(params.printable)

    // Check if the element exists
    if (!printElement) {
      window.console.error('Invalid HTML element id: ' + params.printable)
      return
    }

    // Clone the target element including its children (if available)
    params.printableElement = cloneElement(printElement, params)

    // Add header
    if (params.header) {
      addHeader(params.printableElement, params)
    }

    // Print html element contents
    Print.send(params, printFrame)
  }
}

function cloneElement (element, params) {
  // Clone the main node (if not already inside the recursion process)
  const clone = element.cloneNode()

  // Loop over and process the children elements / nodes (including text nodes)
  const childNodesArray = Array.prototype.slice.call(element.childNodes)
  for (let i = 0; i < childNodesArray.length; i++) {
    // Check if we are skipping the current element
    if (params.ignoreElements.indexOf(childNodesArray[i].id) !== -1) {
      continue
    }

    // Clone the child element
    const clonedChild = cloneElement(childNodesArray[i], params)

    // Attach the cloned child to the cloned parent node
    clone.appendChild(clonedChild)
  }

  // Get all styling for print element (for nodes of type element only)
  if (params.scanStyles && element.nodeType === 1) {
    clone.setAttribute('style', collectStyles(element, params))
  }

  // Check if the element needs any state processing (copy user input data)
  switch (element.tagName) {
    case 'SELECT':
      // Copy the current selection value to its clone
      clone.value = element.value
      break
    case 'CANVAS':
      // Copy the canvas content to its clone
      clone.getContext('2d').drawImage(element, 0, 0)
      break
  }

  return clone
}

function isHtmlElement (printable) {
  // Check if element is instance of HTMLElement or has nodeType === 1 (for elements in iframe)
  return typeof printable === 'object' && printable && (printable instanceof HTMLElement || printable.nodeType === 1)
}
<|endoftext|>