<commit_before>Polymer({
  is: 'paper-dropdown-input',

  behaviors: [
    Polymer.IronFormElementBehavior,
    Polymer.PaperInputBehavior,
    Polymer.Templatizer,
    Polymer.NeonAnimationRunnerBehavior
  ],

  properties: {
    /**
     * The items to show as suggestions.
     * This is either an array of strings or an array of objects, every object must contain at least the value property.
     */
    items: {
      type: Array,
      value: [],
      observer: "_itemsChanged"
    },
    /**
     * The items remaining after filtering
     */
    _filtereditems: {
      type: Array,
      computed: '_filterItems(items, immediateValue)',
      observer: '_updateTemplate'
    },

    /**
     * The value currently in the native input element
     */
    _inputvalue: {
      type: String,
      value: "",
      observer: "_inputvalueChanged"
    },
    /**
     * The immediate value of the input.
     * This value is updated while the user is typing.
     */
    immediateValue: {
      type: String,
      value: "",
      readOnly: true,
      notify: true
    },
    /**
     * The string that represents the current value.
     */
    value: {
      type: String,
      value: "",
      // notify is already fired (twice) by Polymer.IronFormElementBehavior and Polymer.PaperInputBehavior
      // notify: true,
      observer: "_valueChanged"
    },

    /**
     * The stamped instance of your custom template, if present
     */
    _templateInstance: Object,

    /**
     * True if the input is currently active and the dropdown menu is opened
     */
    opened: {
      type: Boolean,
      observer: "_openedChanged"
    },

    /**
     * if true, this input doesn't allow any value that does not exist in the 'items' array
     */
    noFreedom: {
      type: Boolean,
      value: false
    },

    selectedIndex: {
      type: Number,
      notify: true
    },

    animationConfig: {
      type: Object,
      value: function() {
        return {
          'showmenu': [{
            name: 'fade-in-animation',
            node: this.$.menu,
            timing: {
              delay: 150,
              duration: 50
            }
          }, {
            name: 'expand-animation',
            node: this.$.menu,
            timing: {
              delay: 150,
              duration: 200
            }
          }],
          'hidemenu': [{
            name: 'fade-out-animation',
            node: this.$.menu,
            timing: {
              duration: 200
            }
          }]
        }
      }
    }
  },

  listeners: {
    'neon-animation-finish': '_onNeonAnimationFinish'
  },

  observers: ['_selectedIndexChanged(selectedIndex)'],

  /*
   * if the items changed, re-evaluate the current value
   */
  _itemsChanged: function(items) {
    var match = items.find(item => item.value == this.value);
    if (match) {
      this._inputvalue= match.value;
    } else if (this.noFreedom) {
      this._inputvalue= "";
    }
  },

  /**
   * Fires when selectedIndex changes, used to respond to externally triggered changes (el.selectedIndex = x)
   *
   * @param {Number} selectedIndex
   */
  _selectedIndexChanged: function(selectedIndex) {
    if (selectedIndex == undefined) {
      this.clear();
    } else if (this.items && this.items[selectedIndex]) {
      // if the selected index does not match the selected value, update the value
      if (this.value != this.items[selectedIndex]) {
        this._setValueByItem(this.items[selectedIndex]);
      }
    }
  },

  /**
   * Returns a filtered version of items, based on if the array object matched 'immediateValue'
   *
   * @param {Array.<Object>} items the array of items to filter
   * @param {String} immediateValue value to filter with
   * @return {Array.<Object>} the filtered array
   */
  _filterItems: function(items, immediateValue) {
    if (!immediateValue) {
      return items;
    } else {
      var _immediateValue = immediateValue.toLowerCase();
      return items.filter( item => {
        if (!item.value && typeof item != "string") {
          console.error("paper-dropdown-input: item in `items`:", item, " is not a string or does not contain `value` property");
          return true; // everything goes through
        } else {
          return (item.value || item).toLowerCase().indexOf(_immediateValue) > -1;
        }
      });
    }
  },

  /**
   * Fired when user clicks inside the dropdown menu.
   *
   * @param {Event} event the click event
   */
  _onClick: function(event) {
    var selectedItem = Polymer.dom(event).localTarget;
    if (selectedItem) {
      this._setValueByItem(selectedItem);
    }
  },

  /**
   * Sets up the template
   */
  ready: function() {
    var template = Polymer.dom(this).querySelector('template');
    if (!template) {
      template = this.$.menuTemplate;
    }
    this.templatize(template);
    this._templateInstance = this.stamp({
      items: this._filtereditems
    });
    Polymer.dom(this).appendChild(this._templateInstance.root);
    // instance.myProp = 'new value';
  },
  /**
   * Updates the stamped template's data
   *
   * @param {Array.<Object>} filtereditems the new list to display in the dropdown
   */
  _updateTemplate: function(filtereditems) {
    if (this._templateInstance) {
      this._templateInstance.items = filtereditems;
    }
  },

  _openedChanged: function(opened) {
    if (this.opened) {
      this.$.menu.style.display = "block";
      this.playAnimation('showmenu')
    } else {
      this.playAnimation('hidemenu')
    }
  },

  _iconClicked: function() {
    this.clear();
    this.$.input.focus();
  },
  /**
   * Clears the current input
   */
  clear: function() {
    this._inputvalue= "";
  },

  _getIcon: function(immediateValue) {
    return (immediateValue && immediateValue != "") ? "close" : "arrow-drop-down";
  },

  /**
   * Called when animation finishes on the dropdown (when opening or
   * closing). Responsible for "completing" the process of closing
   * the dropdown by setting its display to none.
   */
  _onNeonAnimationFinish: function() {
    if (!this.opened) {
      this.$.menu.style.display = "none";
      this._handleNewValue();
    }
  },

  _setValueByItem: function(item) {
    this._inputvalue = this._getValueByItem(item);
  },

  _getValueByItem: function(item) {
    if (typeof item == "string") {
      return item;
    } else {
      return item.value || item.label || item.textContent.trim();
    }
  },

  /**
   * Processes a new value in 'immediateValue', updates 'value' if allowed (see noFreedom option)
   */
  _handleNewValue: function() {
    // if there is only one match, we auto-fill it
    if (this._filtereditems.length == 1) {
      this._setValueByItem(this._filtereditems[0]);
      this._setImmediateValue(this._inputvalue);
    }

    // just update the value if any input is allowed or if input is blank
    if (!this.noFreedom || this.immediateValue == "") {
      this.value = this.immediateValue;
      this.selectedIndex = this._getSelectedIndexByValue(this.value);

    // if noFreedom is set, check for valid input and revert invalid input
    } else {
      var match = this.items.some( item => {
        if (!item.value && typeof item != 'string') {
          return false
        } else {
          return (item.value || item) == this.immediateValue;
        }
      });

      // reset to original value if no match is found
      if (!match) {
        this._inputvalue = this.value;
      } else {
        this.value = this.immediateValue;
        this.selectedIndex = this._getSelectedIndexByValue(this.value);
      }
    }
  },

  _getSelectedIndexByValue: function(value) {
    for (var i = 0; i < this.items.length; i++) {
      var val = this._getValueByItem(this.items[i]);
      if (val == value) {
        return i;
      }
    }
  },

  _valueChanged: function(newval, oldval) {
    this._inputvalue = newval;
    // _inputvalueChanged might not be fired fast enough by observer alone
    this._inputvalueChanged(newval);
    this._handleNewValue();
  },

  _inputvalueChanged: function(value) {
    this._setImmediateValue(value);
  },

  _arrayIsEmpty: function(array) {
    return array.length == 0;
  }

});
<commit_msg>🛠 fixed bug when items array is undefined<commit_after>Polymer({
  is: 'paper-dropdown-input',

  behaviors: [
    Polymer.IronFormElementBehavior,
    Polymer.PaperInputBehavior,
    Polymer.Templatizer,
    Polymer.NeonAnimationRunnerBehavior
  ],

  properties: {
    /**
     * The items to show as suggestions.
     * This is either an array of strings or an array of objects, every object must contain at least the value property.
     */
    items: {
      type: Array,
      value: [],
      observer: "_itemsChanged"
    },
    /**
     * The items remaining after filtering
     */
    _filtereditems: {
      type: Array,
      computed: '_filterItems(items, immediateValue)',
      observer: '_updateTemplate'
    },

    /**
     * The value currently in the native input element
     */
    _inputvalue: {
      type: String,
      value: "",
      observer: "_inputvalueChanged"
    },
    /**
     * The immediate value of the input.
     * This value is updated while the user is typing.
     */
    immediateValue: {
      type: String,
      value: "",
      readOnly: true,
      notify: true
    },
    /**
     * The string that represents the current value.
     */
    value: {
      type: String,
      value: "",
      // notify is already fired (twice) by Polymer.IronFormElementBehavior and Polymer.PaperInputBehavior
      // notify: true,
      observer: "_valueChanged"
    },

    /**
     * The stamped instance of your custom template, if present
     */
    _templateInstance: Object,

    /**
     * True if the input is currently active and the dropdown menu is opened
     */
    opened: {
      type: Boolean,
      observer: "_openedChanged"
    },

    /**
     * if true, this input doesn't allow any value that does not exist in the 'items' array
     */
    noFreedom: {
      type: Boolean,
      value: false
    },

    selectedIndex: {
      type: Number,
      notify: true
    },

    animationConfig: {
      type: Object,
      value: function() {
        return {
          'showmenu': [{
            name: 'fade-in-animation',
            node: this.$.menu,
            timing: {
              delay: 150,
              duration: 50
            }
          }, {
            name: 'expand-animation',
            node: this.$.menu,
            timing: {
              delay: 150,
              duration: 200
            }
          }],
          'hidemenu': [{
            name: 'fade-out-animation',
            node: this.$.menu,
            timing: {
              duration: 200
            }
          }]
        }
      }
    }
  },

  listeners: {
    'neon-animation-finish': '_onNeonAnimationFinish'
  },

  observers: ['_selectedIndexChanged(selectedIndex)'],

  /*
   * if the items changed, re-evaluate the current value
   */
  _itemsChanged: function(items) {
    if (items) {
      var match = items.find(item => item.value == this.value);
      if (match) {
        this._inputvalue= match.value;
      } else if (this.noFreedom) {
        this._inputvalue= "";
      }
    }
  },

  /**
   * Fires when selectedIndex changes, used to respond to externally triggered changes (el.selectedIndex = x)
   *
   * @param {Number} selectedIndex
   */
  _selectedIndexChanged: function(selectedIndex) {
    if (selectedIndex == undefined) {
      this.clear();
    } else if (this.items && this.items[selectedIndex]) {
      // if the selected index does not match the selected value, update the value
      if (this.value != this.items[selectedIndex]) {
        this._setValueByItem(this.items[selectedIndex]);
      }
    }
  },

  /**
   * Returns a filtered version of items, based on if the array object matched 'immediateValue'
   *
   * @param {Array.<Object>} items the array of items to filter
   * @param {String} immediateValue value to filter with
   * @return {Array.<Object>} the filtered array
   */
  _filterItems: function(items, immediateValue) {
    if (!immediateValue) {
      return items;
    } else {
      var _immediateValue = immediateValue.toLowerCase();
      return items.filter( item => {
        if (!item.value && typeof item != "string") {
          console.error("paper-dropdown-input: item in `items`:", item, " is not a string or does not contain `value` property");
          return true; // everything goes through
        } else {
          return (item.value || item).toLowerCase().indexOf(_immediateValue) > -1;
        }
      });
    }
  },

  /**
   * Fired when user clicks inside the dropdown menu.
   *
   * @param {Event} event the click event
   */
  _onClick: function(event) {
    var selectedItem = Polymer.dom(event).localTarget;
    if (selectedItem) {
      this._setValueByItem(selectedItem);
    }
  },

  /**
   * Sets up the template
   */
  ready: function() {
    var template = Polymer.dom(this).querySelector('template');
    if (!template) {
      template = this.$.menuTemplate;
    }
    this.templatize(template);
    this._templateInstance = this.stamp({
      items: this._filtereditems
    });
    Polymer.dom(this).appendChild(this._templateInstance.root);
    // instance.myProp = 'new value';
  },
  /**
   * Updates the stamped template's data
   *
   * @param {Array.<Object>} filtereditems the new list to display in the dropdown
   */
  _updateTemplate: function(filtereditems) {
    if (this._templateInstance) {
      this._templateInstance.items = filtereditems;
    }
  },

  _openedChanged: function(opened) {
    if (this.opened) {
      this.$.menu.style.display = "block";
      this.playAnimation('showmenu')
    } else {
      this.playAnimation('hidemenu')
    }
  },

  _iconClicked: function() {
    this.clear();
    this.$.input.focus();
  },
  /**
   * Clears the current input
   */
  clear: function() {
    this._inputvalue= "";
  },

  _getIcon: function(immediateValue) {
    return (immediateValue && immediateValue != "") ? "close" : "arrow-drop-down";
  },

  /**
   * Called when animation finishes on the dropdown (when opening or
   * closing). Responsible for "completing" the process of closing
   * the dropdown by setting its display to none.
   */
  _onNeonAnimationFinish: function() {
    if (!this.opened) {
      this.$.menu.style.display = "none";
      this._handleNewValue();
    }
  },

  _setValueByItem: function(item) {
    this._inputvalue = this._getValueByItem(item);
  },

  _getValueByItem: function(item) {
    if (typeof item == "string") {
      return item;
    } else {
      return item.value || item.label || item.textContent.trim();
    }
  },

  /**
   * Processes a new value in 'immediateValue', updates 'value' if allowed (see noFreedom option)
   */
  _handleNewValue: function() {
    // if there is only one match, we auto-fill it
    if (this._filtereditems.length == 1) {
      this._setValueByItem(this._filtereditems[0]);
      this._setImmediateValue(this._inputvalue);
    }

    // just update the value if any input is allowed or if input is blank
    if (!this.noFreedom || this.immediateValue == "") {
      this.value = this.immediateValue;
      this.selectedIndex = this._getSelectedIndexByValue(this.value);

    // if noFreedom is set, check for valid input and revert invalid input
    } else {
      var match = this.items.some( item => {
        if (!item.value && typeof item != 'string') {
          return false
        } else {
          return (item.value || item) == this.immediateValue;
        }
      });

      // reset to original value if no match is found
      if (!match) {
        this._inputvalue = this.value;
      } else {
        this.value = this.immediateValue;
        this.selectedIndex = this._getSelectedIndexByValue(this.value);
      }
    }
  },

  _getSelectedIndexByValue: function(value) {
    for (var i = 0; i < this.items.length; i++) {
      var val = this._getValueByItem(this.items[i]);
      if (val == value) {
        return i;
      }
    }
  },

  _valueChanged: function(newval, oldval) {
    this._inputvalue = newval;
    // _inputvalueChanged might not be fired fast enough by observer alone
    this._inputvalueChanged(newval);
    this._handleNewValue();
  },

  _inputvalueChanged: function(value) {
    this._setImmediateValue(value);
  },

  _arrayIsEmpty: function(array) {
    return array.length == 0;
  }

});
<|endoftext|>


<commit_before>var request = require('superagent');
var Hand = require('./hand');

module.exports = {

  VERSION: "GrandMaster Level 2 RefactorMaster Fixed",

  bet_request: function(gameState, response) {
    try {
      console.log('Actual game state', JSON.stringify(gameState, null, 4));

      var player = gameState.players[gameState.in_action];
      var hand = player.hole_cards;

      var isPreFlop = function(gameState) {
        return gameState.community_cards.length === 0;
      };

      var respond = function(message) {
        response.send(200, message.toString());
      };

      var betToCall = gameState.current_buy_in - player.bet;

      var getPreFlopBet = function(gameState, player, hand) {
        if (hand.rankIsSame() && hand.rankHasBiggerThan(7)) {
          return player.stack;
        }

        if (hand.rankIsSame()) {
          return gameState.minimum_raise * 40;
        }

        if (hand.diffIsOne() && hand.suiteIsSame()) {
          return gameState.minimum_raise * 30;
        }

        if (hand.diffIsOne()) {
          return gameState.minimum_raise * 20;
        }

        if (hand.suiteIsSame()) {
          return gameState.minimum_raise * 10;
        }

        if (player.bet > 0) {
          return betToCall;
        }

        return 0;
      };

      if (isPreFlop(gameState)) {
        respond(getPreFlopBet(gameState, player, new Hand(hand)));
      } else {
        this.fetchHandRank(hand, gameState.community_cards, function(rankResponse) {
          if (rankResponse.rank == 0) {
            respond(0);
          } else if (rankResponse.rank == 1) {
            respond(betToCall);
          } else {
            respond(gameState.minimum_raise * 20);
          }
        });
        respond(gameState.current_buy_in - player.bet);
      }

    } catch (e) {
      console.log('EXCEPTION', e);
    }

    respond(gameState.current_buy_in - player.bet);
  },

  showdown: function(gameState) {
    //console.log('Showdown', JSON.stringify(gameState, null, 4));
  },

  fetchHandRank: function (hand, community_cards, callback) {
    var cards = hand.concat(community_cards);
    request
        .get('http://rainman.leanpoker.org/rank')
        .send('cards=' + JSON.stringify(cards))
        .end(function (err, res) {
          callback(JSON.parse(res.text));
        });
  }
};
<commit_msg>error logging added<commit_after>var request = require('superagent');
var Hand = require('./hand');

module.exports = {

  VERSION: "GrandMaster Level 2 RefactorMaster Fixed",

  bet_request: function(gameState, response) {
    try {
      console.log('Actual game state', JSON.stringify(gameState, null, 4));

      var player = gameState.players[gameState.in_action];
      var hand = player.hole_cards;

      var isPreFlop = function(gameState) {
        return gameState.community_cards.length === 0;
      };

      var respond = function(message) {
        response.send(200, message.toString());
      };

      var betToCall = gameState.current_buy_in - player.bet;

      var getPreFlopBet = function(gameState, player, hand) {
        if (hand.rankIsSame() && hand.rankHasBiggerThan(7)) {
          return player.stack;
        }

        if (hand.rankIsSame()) {
          return gameState.minimum_raise * 40;
        }

        if (hand.diffIsOne() && hand.suiteIsSame()) {
          return gameState.minimum_raise * 30;
        }

        if (hand.diffIsOne()) {
          return gameState.minimum_raise * 20;
        }

        if (hand.suiteIsSame()) {
          return gameState.minimum_raise * 10;
        }

        if (player.bet > 0) {
          return betToCall;
        }

        return 0;
      };

      if (isPreFlop(gameState)) {
        respond(getPreFlopBet(gameState, player, new Hand(hand)));
      } else {
        this.fetchHandRank(hand, gameState.community_cards, function(rankResponse) {
          if (rankResponse.rank == 0) {
            respond(0);
          } else if (rankResponse.rank == 1) {
            respond(betToCall);
          } else {
            respond(gameState.minimum_raise * 20);
          }
        });
        respond(gameState.current_buy_in - player.bet);
      }

    } catch (e) {
      console.log('EXCEPTION', e);
    }

    respond(gameState.current_buy_in - player.bet);
  },

  showdown: function(gameState) {
    //console.log('Showdown', JSON.stringify(gameState, null, 4));
  },

  fetchHandRank: function (hand, community_cards, callback) {
    var cards = hand.concat(community_cards);
    request
        .get('http://rainman.leanpoker.org/rank')
        .send('cards=' + JSON.stringify(cards))
        .end(function (err, res) {
          console.log("rainmain", res.text);
          callback(JSON.parse(res.text));
        });
  }
};
<|endoftext|>


<commit_before>/**
 * Created by nuintun on 2015/11/20.
 */

'use strict';

var ipc = require('ipc-renderer');

var fs = require('fs');
var path = require('path');
var util = require('../../util');
var Vue = require('../../vue/vue');
var Terminal = require('../../terminal');

const EMPTYPROJECT = {
  name: '',
  path: '',
  env: [],
  command: [],
  empty: true
};

require('../project-configure');

/**
 * clone project
 * @param projects
 * @param index
 * @returns {*}
 */
function clone(projects, index){
  return util.clone(projects[index] || EMPTYPROJECT);
}

/**
 * srcoll
 * @param xterm
 */
function scroll(xterm){
  var parent = xterm.element.parentNode;

  if (parent) {
    var height = (xterm.y + 2) * 18;
    var scrollTop = parent.scrollTop;
    var viewHeight = parent.clientHeight;

    if (scrollTop > height || height > scrollTop + viewHeight) {
      parent.scrollTop = Math.max(0, height - viewHeight);
    }
  }
}

/**
 * createXTerm
 * @param name
 * @param xtermNode
 */
function createXTerm(name, xtermNode){
  var runtime = window.AppRuntime[name];

  function refresh(xterm){
    if (xtermNode.firstChild !== xterm.element) {
      if (xtermNode.firstChild) {
        xtermNode.removeChild(xtermNode.firstChild);
      }

      xterm.focus();
      xtermNode.appendChild(xterm.element);
      scroll(xterm);
    }
  }

  if (runtime) {
    refresh(runtime.xterm);
  } else {
    var xterm = new Terminal({
      scrollback: 80,
      convertEol: true,
      fgColor: 'inherit',
      bgColor: 'transparent'
    });

    xterm.open();

    refresh(xterm);

    window.AppRuntime[name] = {
      xterm: xterm
    };
  }
}

module.exports = Vue.component('app-main', {
  template: fs.readFileSync(path.join(__dirname, 'app-main.html')).toString(),
  props: {
    activeIndex: {
      type: Number,
      twoWay: true,
      required: true
    },
    projects: {
      type: Array,
      twoWay: true,
      required: true
    },
    unique: {
      type: Object,
      required: true
    }
  },
  data: function (){
    var project = clone(this.projects, this.activeIndex);

    return {
      showSetting: false,
      expandCommand: false,
      project: project,
      command: project.command.slice(0, 3),
      moreCommand: project.command.slice(3)
    };
  },
  watch: {
    projects: function (){
      this.project = clone(this.projects, this.activeIndex);
    },
    activeIndex: function (){
      this.project = clone(this.projects, this.activeIndex);
    },
    project: function (project){
      this.command = project.command.slice(0, 3);
      this.moreCommand = project.command.slice(3);

      createXTerm(project.name, this.$els.terminal);
    }
  },
  methods: {
    exec: function (name, command){
      ipc.send('emulator', {
        name: this.project.name,
        path: this.project.path,
        env: util.normalize(this.project.env),
        command: {
          name: name,
          value: command
        }
      }, 'start');
    },
    setting: function (){
      this.showSetting = true;
    },
    remove: function (){
      var runtime = window.AppRuntime[this.project.name];

      if (runtime) {
        runtime.xterm.close();

        delete window.AppRuntime[this.project.name];
      }

      // remove project
      this.projects.splice(this.activeIndex, 1);

      // change active
      this.activeIndex = 0;

      // save configure
      this.$dispatch('save-configure');
    }
  },
  events: {
    'setting-toggle': function (state){
      this.showSetting = state;
    },
    edit: function (project){
      if (project.name !== this.project.name) {
        window.AppRuntime[project.name] = window.AppRuntime[this.project.name];

        delete window.AppRuntime[this.project.name];
      }

      this.projects.$set(this.activeIndex, project);
      this.$dispatch('save-configure');
    }
  },
  created: function (){
    var context = this;

    document.addEventListener('click', function (event){
      var target = event.target;
      var trigger = context.$els.expandTrigger;

      context.expandCommand = trigger && trigger.contains(target);
    }, false);

    var step = 0;

    ipc.on('emulator', function (event, type, project, data){
      var delay = step * 16;
      var runtime = window.AppRuntime[project.name];

      if (runtime) {
        switch (type) {
          case 'data':
            data += '';
            step++;
            break;
          case 'error':
            data = '执行出现错误: ' + data;
            step = 0;
            break;
          case 'close':
            data = '\u001b[32m命令执行完毕\u001b[39m\r\n';
            step = 0;
            break;
        }

        setTimeout(function (){
          runtime.xterm.write(data + '');
          scroll(runtime.xterm);
        }, delay);
      } else {
        event.sender.send('emulator', project, 'stop');
      }
    });
  },
  ready: function (){
    createXTerm(this.project.name, this.$els.terminal);
  }
});
<commit_msg>update files<commit_after>/**
 * Created by nuintun on 2015/11/20.
 */

'use strict';

var ipc = require('ipc-renderer');

var fs = require('fs');
var path = require('path');
var util = require('../../util');
var Vue = require('../../vue/vue');
var Terminal = require('../../terminal');

const EMPTYPROJECT = {
  name: '',
  path: '',
  env: [],
  command: [],
  empty: true
};

require('../project-configure');

/**
 * clone project
 * @param projects
 * @param index
 * @returns {*}
 */
function clone(projects, index){
  return util.clone(projects[index] || EMPTYPROJECT);
}

/**
 * srcoll
 * @param xterm
 */
function scroll(xterm){
  var parent = xterm.element.parentNode;

  if (parent) {
    var height = (xterm.y + 2) * 18;
    var scrollTop = parent.scrollTop;
    var viewHeight = parent.clientHeight;

    if (scrollTop > height || height > scrollTop + viewHeight) {
      parent.scrollTop = Math.max(0, height - viewHeight);
    }
  }
}

/**
 * createXTerm
 * @param name
 * @param xtermNode
 */
function createXTerm(name, xtermNode){
  var runtime = window.AppRuntime[name];

  function refresh(xterm){
    if (xtermNode.firstChild !== xterm.element) {
      if (xtermNode.firstChild) {
        xtermNode.removeChild(xtermNode.firstChild);
      }

      xterm.focus();
      xtermNode.appendChild(xterm.element);
      scroll(xterm);
    }
  }

  if (runtime) {
    refresh(runtime.xterm);
  } else {
    var xterm = new Terminal({
      scrollback: 80,
      convertEol: true,
      fgColor: 'inherit',
      bgColor: 'transparent'
    });

    xterm.open();

    refresh(xterm);

    window.AppRuntime[name] = {
      xterm: xterm
    };
  }
}

module.exports = Vue.component('app-main', {
  template: fs.readFileSync(path.join(__dirname, 'app-main.html')).toString(),
  props: {
    activeIndex: {
      type: Number,
      twoWay: true,
      required: true
    },
    projects: {
      type: Array,
      twoWay: true,
      required: true
    },
    unique: {
      type: Object,
      required: true
    }
  },
  data: function (){
    var project = clone(this.projects, this.activeIndex);

    return {
      showSetting: false,
      expandCommand: false,
      project: project,
      command: project.command.slice(0, 3),
      moreCommand: project.command.slice(3)
    };
  },
  watch: {
    projects: function (){
      this.project = clone(this.projects, this.activeIndex);
    },
    activeIndex: function (){
      this.project = clone(this.projects, this.activeIndex);
    },
    project: function (project){
      this.command = project.command.slice(0, 3);
      this.moreCommand = project.command.slice(3);

      createXTerm(project.name, this.$els.terminal);
    }
  },
  methods: {
    exec: function (name, command){
      ipc.send('emulator', {
        name: this.project.name,
        path: this.project.path,
        env: util.normalize(this.project.env),
        command: {
          name: name,
          value: command
        }
      }, 'start');
    },
    setting: function (){
      this.showSetting = true;
    },
    remove: function (){
      var runtime = window.AppRuntime[this.project.name];

      if (runtime) {
        runtime.xterm.close();

        delete window.AppRuntime[this.project.name];
      }

      // remove project
      this.projects.splice(this.activeIndex, 1);

      // change active
      this.activeIndex = 0;

      // save configure
      this.$dispatch('save-configure');
    }
  },
  events: {
    'setting-toggle': function (state){
      this.showSetting = state;
    },
    edit: function (project){
      if (project.name !== this.project.name) {
        window.AppRuntime[project.name] = window.AppRuntime[this.project.name];

        delete window.AppRuntime[this.project.name];
      }

      this.projects.$set(this.activeIndex, project);
      this.$dispatch('save-configure');
    }
  },
  created: function (){
    var context = this;

    document.addEventListener('click', function (event){
      var target = event.target;
      var trigger = context.$els.expandTrigger;

      context.expandCommand = trigger && trigger.contains(target);
    }, false);

    var step = 0;

    ipc.on('emulator', function (event, type, project, data){
      var delay = step * 20;
      var runtime = window.AppRuntime[project.name];

      if (runtime) {
        switch (type) {
          case 'data':
            data += '';
            step++;
            break;
          case 'error':
            data = '执行出现错误: ' + data;
            step = 0;
            break;
          case 'close':
            data = '\u001b[32m命令执行完毕\u001b[39m\r\n';
            step = 0;
            break;
        }

        setTimeout(function (){
          runtime.xterm.write(data + '');
          scroll(runtime.xterm);
        }, delay);
      } else {
        event.sender.send('emulator', project, 'stop');
      }
    });
  },
  ready: function (){
    createXTerm(this.project.name, this.$els.terminal);
  }
});
<|endoftext|>


<commit_before>"use strict";

import React, {Component} from 'react';
import _ from 'lodash'
import {RenderForm, Form, Textarea, CharField, RegexField, SlugField, EmailField, URLField, FilePathField, GenericIPAddressField, ChoiceField, DateField, DateTimeField, BooleanField, IntegerField, FloatField, FileField, MultipleFileField, ImageField} from 'newforms'
import BootstrapForm, {Container, Row, Col, Field} from 'newforms-bootstrap'
import Agenda from '../lib/agenda'
import Calendar from './calendar'
import {DatePicker} from './date-picker'

export default class extends Component {
  constructor(props) {
    super(props)
    let now;
    this.state = {
      startPicker: { show: false, date: now },
      endPicker: { show: false, date: now }
    }
    this.createForm()
  }

  createForm() {
    let MyForm = Form.extend({
      title: CharField({initial: this.props.title||''}),
      content: CharField({
        required: false,
        widget: Textarea,
        initial: this.props.content||''
      }),
      room: ChoiceField({
        choices: this.props.rooms.map(r => [r.id, r.name]),
        initial: this.props.room ? this.props.room.id : this.props.rooms[0].id
      }),
      start: DateTimeField({
        initial: this.props.start.date ? this.props.start.date : new Date(this.props.start),
        widgetAttrs: { onClick: this._showDatePicker.bind(this) }
      }),
      end: DateTimeField({
        initial: this.props.end.date ? this.props.end.date : new Date(this.props.end),
        widgetAttrs: { onClick: this._showDatePicker.bind(this) }
      })
    })
    this.form = new MyForm({
      controlled: true,
      onChange: this.onFormChange.bind(this),
      validation: 'auto'
    })
  }
  onFormChange() {
    this.forceUpdate()
  }

  _showDatePicker(e) {
    e.preventDefault()
    this.changeDate(new Date(e.target.value), e.target.name)
  }

  changeDate(date, name, toggle=true) {
    let common = {
      year: date.getFullYear(),
      month: date.getMonth(),
      day: date.getDate(),
      hour: date.getHours(),
      name: name,
      except: this.props.except||[]
    }
    this.setState(
      name === 'start' ?
      {startPicker: _.extend({
        show: toggle ? !this.state.startPicker.show : this.state.startPicker.show,
        view: "month"
      }, common)}
      :
      {endPicker: _.extend({
        show: toggle ? !this.state.endPicker.show : this.state.startPicker.show,
        view: "week"
      }, common)})
  }
  _onSelectStart(val) {
    let form = this.mForm.getForm()
      , start = new Date(val.date)
    form.updateData({ start })
    this.changeDate(start, "start")
  }

  _onSelectEnd(val) {
    let form = this.mForm.getForm()
      , end = new Date(val.date)
    form.updateData({ end })
    this.changeDate(end, "end")
  }

  _onSubmit(e) {
    e.preventDefault();
    let form = this.mForm.getForm()
    if(form.validate())
      this.props.onSubmit(form.cleanedData, this.props.id)
  }
  _onCancel(e) {
    e.preventDefault();
    this.props.onCancel()
  }
  render() {
    return (
      <form encType="multipart/form-data" className="agenda-modal">
        <RenderForm form={this.form} ref={(ref) => this.mForm = ref}>
          <Container autoColumns="md">

Event

              <button className="btn btn-default" onClick={this._onSubmit.bind(this)}>Save</button>
              <button className="btn btn-default" onClick={this._onCancel.bind(this)}>Cancel</button>

            <Row>
              <Field name="title" md="8"/>
              <Field name="room"/>
            </Row>
            <Row>
              <Field name="content"/>
            </Row>
            <Row>
              <Field name="start" md="6" />
              <Field name="end" />
            </Row>
            <Row>
              <Col md="6">
                {this.state.startPicker.show &&
                  <DatePicker toggle {...this.state.startPicker} onSelect={this._onSelectStart.bind(this)}  />}
              </Col>
              <Col>
                {this.state.endPicker.show &&
                  <DatePicker toggle {...this.state.endPicker} onSelect={this._onSelectEnd.bind(this)}  />}
              </Col>
            </Row>
          </Container>
        </RenderForm>
      </form>
    )
  }
}
<commit_msg>modal: replace datepicker by dateTimePicker<commit_after>"use strict";

import React, {Component} from 'react';
import _ from 'lodash'
import {RenderForm, Form, Textarea, CharField, RegexField, SlugField, EmailField, URLField, FilePathField, GenericIPAddressField, ChoiceField, DateField, DateTimeField, BooleanField, IntegerField, FloatField, FileField, MultipleFileField, ImageField} from 'newforms'
import BootstrapForm, {Container, Row, Col, Field} from 'newforms-bootstrap'
import Agenda from '../lib/agenda'
import Calendar from './calendar'
import DateTimePicker from './date-time-picker'

export default class extends Component {
  constructor(props) {
    super(props)
    let now;
    this.state = {
      startPicker: { show: false, date: now },
      endPicker: { show: false, date: now }
    }
    this.createForm()
  }

  createForm() {
    let MyForm = Form.extend({
      title: CharField({initial: this.props.title||''}),
      content: CharField({
        required: false,
        widget: Textarea,
        initial: this.props.content||''
      }),
      room: ChoiceField({
        choices: this.props.rooms.map(r => [r.id, r.name]),
        initial: this.props.room ? this.props.room.id : this.props.rooms[0].id
      }),
      start: DateTimeField({
        initial: this.props.start.date ? this.props.start.date : new Date(this.props.start),
        widgetAttrs: { onClick: this._showDatePicker.bind(this) }
      }),
      end: DateTimeField({
        initial: this.props.end.date ? this.props.end.date : new Date(this.props.end),
        widgetAttrs: { onClick: this._showDatePicker.bind(this) }
      })
    })
    this.form = new MyForm({
      controlled: true,
      onChange: this.onFormChange.bind(this),
      validation: 'auto'
    })
  }
  onFormChange() {
    this.forceUpdate()
  }

  _showDatePicker(e) {
    e.preventDefault()
    this.changeDate(new Date(e.target.value), e.target.name)
  }

  changeDate(date, name, toggle=true) {
    let common = {
      year: date.getFullYear(),
      month: date.getMonth(),
      day: date.getDate(),
      hour: date.getHours(),
      minute: date.getMinutes(),
      name: name,
      except: this.props.except||[]
    }
    this.setState(
      name === 'start' ?
      {startPicker: _.extend({
        show: toggle ? !this.state.startPicker.show : this.state.startPicker.show,
        view: "month"
      }, common)}
      :
      {endPicker: _.extend({
        show: toggle ? !this.state.endPicker.show : this.state.startPicker.show,
        view: "week"
      }, common)})
  }
  _onSelectStart(val) {
    let form = this.mForm.getForm()
      , start = new Date(val.date)
    form.updateData({ start })
    this.changeDate(start, "start")
  }

  _onSelectEnd(val) {
    let form = this.mForm.getForm()
      , end = new Date(val.date)
    form.updateData({ end })
    this.changeDate(end, "end")
  }

  _onSubmit(e) {
    e.preventDefault();
    let form = this.mForm.getForm()
    if(form.validate())
      this.props.onSubmit(form.cleanedData, this.props.id)
  }
  _onCancel(e) {
    e.preventDefault();
    this.props.onCancel()
  }
  render() {
    return (
      <form encType="multipart/form-data" className="agenda-modal">
        <RenderForm form={this.form} ref={(ref) => this.mForm = ref}>
          <Container autoColumns="md">

Event

              <button className="btn btn-default" onClick={this._onSubmit.bind(this)}>Save</button>
              <button className="btn btn-default" onClick={this._onCancel.bind(this)}>Cancel</button>

            <Row>
              <Field name="title" md="8"/>
              <Field name="room"/>
            </Row>
            <Row>
              <Field name="content"/>
            </Row>
            <Row>
              <Field name="start" md="6" />
              <Field name="end" />
            </Row>
            <Row>
              <Col md="6">
                {this.state.startPicker.show &&
                  <DateTimePicker {...this.state.startPicker}
                                  onSelect={this._onSelectStart.bind(this)}  />}
              </Col>
              <Col>
                {this.state.endPicker.show &&
                  <DateTimePicker {...this.state.endPicker}
                                  onSelect={this._onSelectEnd.bind(this)}  />}
              </Col>
            </Row>
          </Container>
        </RenderForm>
      </form>
    )
  }
}
<|endoftext|>


<commit_before>/*jslint nomen: true*/
// Yith Library Server is a password storage server.
// Copyright (C) 2012 Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com>
//
// This file is part of Yith Library Server.
//
// Yith Library Server is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Yith Library Server is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Yith Library Server.  If not, see <http://www.gnu.org/licenses/>.

(function ($) {
    "use strict";

    $.mozilla_persona = function (options) {
        var current_user = options.current_user,
            current_provider = options.current_provider;

        if (!current_user || current_provider !== 'persona') {
            current_user = null;
        }

        if (options.login_selector) {
            $(options.login_selector).click(function (event) {
                event.preventDefault();
                navigator.id.request();
            });
        }

        if (options.logout_selector) {
            $(options.logout_selector).click(function (event) {
                if (current_provider === 'persona') {
                    event.preventDefault();
                    navigator.id.logout();
                }
            });
        }

        navigator.id.watch({
            loggedInUser: current_user,
            onlogin: function (assertion) {
                var form = [
                    "<form class='hide' ",
                    "action='" + options.login_url + "' ",
                    "method='post'>",
                    "<input type='hidden' name='assertion' ",
                    "value='" + assertion + "'/>",
                    (options.next_url ? "<input type='hidden' name='next_url' value='" + options.next_url + "' />" : ""),
                    "</form>"
                ].join("");
                $(form).appendTo("body").submit();
            },
            onlogout: function () {
                if (current_provider === 'persona') {
                    if (typeof options.logout_url === 'string') {
                        window.location = options.logout_url;
                    } else {
                        options.logout_url();
                    }
                }
            }
        });

    };

}(jQuery));
<commit_msg>Fix redirection after login<commit_after>/*jslint nomen: true*/
// Yith Library Server is a password storage server.
// Copyright (C) 2012 Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com>
//
// This file is part of Yith Library Server.
//
// Yith Library Server is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Yith Library Server is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Yith Library Server.  If not, see <http://www.gnu.org/licenses/>.

(function ($) {
    "use strict";

    $.mozilla_persona = function (options) {
        var current_user = options.current_user,
            current_provider = options.current_provider;

        if (!current_user || current_provider !== 'persona') {
            current_user = null;
        }

        if (options.login_selector) {
            $(options.login_selector).click(function (event) {
                event.preventDefault();
                navigator.id.request();
            });
        }

        if (options.logout_selector) {
            $(options.logout_selector).click(function (event) {
                if (current_provider === 'persona') {
                    event.preventDefault();
                    navigator.id.logout();
                }
            });
        }

        navigator.id.watch({
            loggedInUser: current_user,
            onlogin: function (assertion) {
                var form = [
                    "<form class='hide' ",
                    "action='" + options.login_url + "' ",
                    "method='post'>",
                    "<input type='hidden' name='assertion' ",
                    "value='" + assertion + "'/>",
                    (options.next_url ? "<input type='hidden' name='next_url' value='" + decodeURIComponent(options.next_url) + "' />" : ""),
                    "</form>"
                ].join("");
                $(form).appendTo("body").submit();
            },
            onlogout: function () {
                if (current_provider === 'persona') {
                    if (typeof options.logout_url === 'string') {
                        window.location = options.logout_url;
                    } else {
                        options.logout_url();
                    }
                }
            }
        });

    };

}(jQuery));
<|endoftext|>


<commit_before>const {exec} = require('child_process');
const {EventEmitter} = require('events');

const {app} = require('electron');
const defaultShell = require('default-shell');

const {getDecoratedEnv} = require('./plugins');
const {productName, version} = require('./package');
const config = require('./config');

let spawn;
try {
  spawn = require('child_pty').spawn;
} catch (err) {
  console.error(
    'A native module failed to load. Typically this means ' +
    'you installed the modules incorrectly.\n Use `scripts/install.sh` ' +
    'to trigger the installation.\n ' +
    'More information: https://github.com/zeit/hyperterm/issues/72'
  );
}

const TITLE_POLL_INTERVAL = 500;

const envFromConfig = config.getConfig().env || {};

module.exports = class Session extends EventEmitter {

  constructor({rows, cols: columns, cwd, shell, shellArgs}) {
    super();
    const baseEnv = Object.assign({}, process.env, {
      LANG: app.getLocale().replace('-', '_') + '.UTF-8',
      TERM: 'xterm-256color',
      TERM_PROGRAM: productName,
      TERM_PROGRAM_VERSION: version
    }, envFromConfig);

    const defaultShellArgs = ['--login'];

    this.pty = spawn(shell || defaultShell, shellArgs || defaultShellArgs, {
      columns,
      rows,
      cwd,
      env: getDecoratedEnv(baseEnv)
    });

    this.pty.stdout.on('data', data => {
      if (this.ended) {
        return;
      }
      this.emit('data', data.toString('utf8'));
    });

    this.pty.on('exit', () => {
      if (!this.ended) {
        this.ended = true;
        this.emit('exit');
      }
    });

    this.shell = shell || defaultShell;
    this.getTitle();
  }

  focus() {
    this.subscribed = true;
    this.getTitle();
  }

  blur() {
    this.subscribed = false;
    clearTimeout(this.titlePoll);
  }

  getTitle() {
    if (process.platform === 'win32') {
      return;
    }

    if (this.fetching) {
      return;
    }
    this.fetching = true;

    let tty = this.pty.stdout.ttyname;
    tty = tty.replace(/^\/dev\/tty/, '');

    // try to exclude grep from the results
    // by grepping for `[s]001` instead of `s001`
    tty = `[${tty[0]}]${tty.substr(1)}`;

    // TODO: limit the concurrency of how many processes we run?
    // TODO: only tested on mac
    exec(`ps uxac | grep ${tty} | head -n 1`, (err, out) => {
      this.fetching = false;
      if (this.ended) {
        return;
      }
      if (err) {
        return;
      }
      let title = out.split(' ').pop();
      if (title) {
        title = title.replace(/^\(/, '');
        title = title.replace(/\)?\n$/, '');
        if (title !== this.lastTitle) {
          this.emit('title', title);
          this.lastTitle = title;
        }
      }

      if (this.subscribed) {
        this.titlePoll = setTimeout(() => this.getTitle(), TITLE_POLL_INTERVAL);
      }
    });
  }

  exit() {
    this.destroy();
  }

  write(data) {
    this.pty.stdin.write(data);
  }

  resize({cols: columns, rows}) {
    try {
      this.pty.stdout.resize({columns, rows});
    } catch (err) {
      console.error(err.stack);
    }
  }

  destroy() {
    try {
      this.pty.kill('SIGHUP');
    } catch (err) {
      console.error('exit error', err.stack);
    }
    this.emit('exit');
    this.ended = true;
    this.blur();
  }

};
<commit_msg>Preserve multi-byte encoded utf-8 characters (#769)<commit_after>const {exec} = require('child_process');
const {EventEmitter} = require('events');
const {StringDecoder} = require('string_decoder');

const {app} = require('electron');
const defaultShell = require('default-shell');

const {getDecoratedEnv} = require('./plugins');
const {productName, version} = require('./package');
const config = require('./config');

let spawn;
try {
  spawn = require('child_pty').spawn;
} catch (err) {
  console.error(
    'A native module failed to load. Typically this means ' +
    'you installed the modules incorrectly.\n Use `scripts/install.sh` ' +
    'to trigger the installation.\n ' +
    'More information: https://github.com/zeit/hyperterm/issues/72'
  );
}

const TITLE_POLL_INTERVAL = 500;

const envFromConfig = config.getConfig().env || {};

module.exports = class Session extends EventEmitter {

  constructor({rows, cols: columns, cwd, shell, shellArgs}) {
    super();
    const baseEnv = Object.assign({}, process.env, {
      LANG: app.getLocale().replace('-', '_') + '.UTF-8',
      TERM: 'xterm-256color',
      TERM_PROGRAM: productName,
      TERM_PROGRAM_VERSION: version
    }, envFromConfig);

    const decoder = new StringDecoder('utf8');

    const defaultShellArgs = ['--login'];

    this.pty = spawn(shell || defaultShell, shellArgs || defaultShellArgs, {
      columns,
      rows,
      cwd,
      env: getDecoratedEnv(baseEnv)
    });

    this.pty.stdout.on('data', data => {
      if (this.ended) {
        return;
      }
      this.emit('data', decoder.write(data));
    });

    this.pty.on('exit', () => {
      if (!this.ended) {
        this.ended = true;
        this.emit('exit');
      }
    });

    this.shell = shell || defaultShell;
    this.getTitle();
  }

  focus() {
    this.subscribed = true;
    this.getTitle();
  }

  blur() {
    this.subscribed = false;
    clearTimeout(this.titlePoll);
  }

  getTitle() {
    if (process.platform === 'win32') {
      return;
    }

    if (this.fetching) {
      return;
    }
    this.fetching = true;

    let tty = this.pty.stdout.ttyname;
    tty = tty.replace(/^\/dev\/tty/, '');

    // try to exclude grep from the results
    // by grepping for `[s]001` instead of `s001`
    tty = `[${tty[0]}]${tty.substr(1)}`;

    // TODO: limit the concurrency of how many processes we run?
    // TODO: only tested on mac
    exec(`ps uxac | grep ${tty} | head -n 1`, (err, out) => {
      this.fetching = false;
      if (this.ended) {
        return;
      }
      if (err) {
        return;
      }
      let title = out.split(' ').pop();
      if (title) {
        title = title.replace(/^\(/, '');
        title = title.replace(/\)?\n$/, '');
        if (title !== this.lastTitle) {
          this.emit('title', title);
          this.lastTitle = title;
        }
      }

      if (this.subscribed) {
        this.titlePoll = setTimeout(() => this.getTitle(), TITLE_POLL_INTERVAL);
      }
    });
  }

  exit() {
    this.destroy();
  }

  write(data) {
    this.pty.stdin.write(data);
  }

  resize({cols: columns, rows}) {
    try {
      this.pty.stdout.resize({columns, rows});
    } catch (err) {
      console.error(err.stack);
    }
  }

  destroy() {
    try {
      this.pty.kill('SIGHUP');
    } catch (err) {
      console.error('exit error', err.stack);
    }
    this.emit('exit');
    this.ended = true;
    this.blur();
  }

};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const WIDGET_DEFAULT_STYLE =
		"position: fixed; z-index: 2147483647; top: 0px; left: 0px; " +
		"margin: 2px; padding: 4px 4px 2px 4px; " +
		"background-color: #FFF; color: #000; " +
		"border: solid 1px #808080; border-radius: 4px; " +
		"font: normal 12px \"Lucida Console\", \"Monaco\", monospace; " +
		"user-select: none; -moz-user-select: none;";

const WIDGET_SIZES = ["10px", "12px", "14px", "16px"];

const WIDGET_LOCATIONS = [
	["0px", "", "0px", ""],
	["0px", "", "", "0px"],
	["", "0px", "", "0px"],
	["", "0px", "0px", ""]
];

var timerWidget;

// Create timer widget
//
function createTimerWidget() {
	timerWidget = document.createElement("div");
	timerWidget.setAttribute("style", WIDGET_DEFAULT_STYLE);
	document.body.appendChild(timerWidget);
}

// Update timer widget
//
function updateTimerWidget(text, size, location) {
	if (!timerWidget) {
		createTimerWidget();
	}

	if (!text) {
		timerWidget.hidden = true;
	} else {
		timerWidget.innerText = text;
		if (size >= 0 && size < WIDGET_SIZES.length) {
			timerWidget.style.fontSize = WIDGET_SIZES[size];
		}
		if (location >= 0 && location < WIDGET_LOCATIONS.length) {
			timerWidget.style.top = WIDGET_LOCATIONS[location][0];
			timerWidget.style.bottom = WIDGET_LOCATIONS[location][1];
			timerWidget.style.left = WIDGET_LOCATIONS[location][2];
			timerWidget.style.right = WIDGET_LOCATIONS[location][3];
		}
		timerWidget.hidden = false;
	}
}

/*** EVENT HANDLERS BEGIN HERE ***/

function handleMessage(message, sender) {
	if (message.type == "timer") {
		updateTimerWidget(message.text, message.size, message.location);
	}
}

browser.runtime.onMessage.addListener(handleMessage);
<commit_msg>Improve timer widget code<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const WIDGET_DEFAULT_STYLE =
		"position: fixed; z-index: 2147483647; top: 0px; left: 0px; " +
		"margin: 2px; padding: 4px 4px 2px 4px; " +
		"background-color: #FFF; color: #000; " +
		"border: solid 1px #808080; border-radius: 4px; " +
		"font: normal 12px \"Lucida Console\", \"Monaco\", monospace; " +
		"user-select: none; -moz-user-select: none;";

const WIDGET_SIZES = ["10px", "12px", "14px", "16px"];

const WIDGET_LOCATIONS = [
	["0px", "", "0px", ""],
	["0px", "", "", "0px"],
	["", "0px", "", "0px"],
	["", "0px", "0px", ""]
];

var timerWidget;

// Update timer widget
//
function updateTimerWidget(text, size, location) {
	if (!text) {
		if (timerWidget) {
			// Hide widget
			timerWidget.hidden = true;
		}
	} else {
		if (!timerWidget) {
			// Create widget
			timerWidget = document.createElement("div");
			timerWidget.setAttribute("style", WIDGET_DEFAULT_STYLE);
			document.body.appendChild(timerWidget);
		}

		// Set text
		timerWidget.innerText = text;

		// Set size
		if (size >= 0 && size < WIDGET_SIZES.length) {
			timerWidget.style.fontSize = WIDGET_SIZES[size];
		}

		// Set location
		if (location >= 0 && location < WIDGET_LOCATIONS.length) {
			timerWidget.style.top = WIDGET_LOCATIONS[location][0];
			timerWidget.style.bottom = WIDGET_LOCATIONS[location][1];
			timerWidget.style.left = WIDGET_LOCATIONS[location][2];
			timerWidget.style.right = WIDGET_LOCATIONS[location][3];
		}

		// Show widget
		timerWidget.hidden = false;
	}
}

/*** EVENT HANDLERS BEGIN HERE ***/

function handleMessage(message, sender) {
	if (message.type == "timer") {
		updateTimerWidget(message.text, message.size, message.location);
	}
}

browser.runtime.onMessage.addListener(handleMessage);
<|endoftext|>


<commit_before>module.exports = function(dirs, config, app, tasks) {

	var
	    gulp       	 = require('gulp'),
    	autoprefixer = require('autoprefixer'),
			extend       = require('extend'),
	    plumber      = require('gulp-plumber'),
	    sourcemaps   = require('gulp-sourcemaps'),
	    postcss      = require('gulp-postcss'),
	    sass         = require('gulp-sass');

	tasks.styles = {
		run: function(isDev) {
			if (!dirs.src.styles.main)
				return Promise.resolve();

			var configStyles = extend(true, config.styles.common, config.styles[isDev ? 'dev': 'prod']);

			var ret = gulp.src(dirs.src.styles.main + '*.scss')
			  .pipe(plumber({
			    errorHandler: function (error) {
			      console.log(error.message);
			      this.emit('end');
			    }
			  }));

			if (configStyles.sourceMaps) {
			  ret = ret
			    .pipe(sourcemaps.init());
			}

			var ret = ret
			      .pipe(sass(configStyles.sass).on('error', sass.logError));

			ret = ret
			  .pipe(postcss([ autoprefixer({ browsers: configStyles.autoprefixer.browsers }) ]));

			if (configStyles.sourceMaps) {
			  ret = ret
			    .pipe(sourcemaps.write({ sourceRoot: configStyles.sourceMapsRoot }));
			}

			ret = ret
			  .pipe(gulp.dest(dirs.dist.styles));

		  return ret;
		}
	}
}<commit_msg>styles sourcemaps inline<commit_after>module.exports = function(dirs, config, app, tasks) {

	var
	    gulp       	 = require('gulp'),
    	autoprefixer = require('autoprefixer'),
			extend       = require('extend'),
	    plumber      = require('gulp-plumber'),
	    sourcemaps   = require('gulp-sourcemaps'),
	    postcss      = require('gulp-postcss'),
	    sass         = require('gulp-sass');

	tasks.styles = {
		run: function(isDev) {
			if (!dirs.src.styles.main)
				return Promise.resolve();

			var configStyles = extend(true, config.styles.common, config.styles[isDev ? 'dev': 'prod']);

			var ret = gulp.src(dirs.src.styles.main + '*.scss')
			  .pipe(plumber({
			    errorHandler: function (error) {
			      console.log(error.message);
			      this.emit('end');
			    }
			  }));

			if (configStyles.sourceMaps) {
			  ret = ret
			    .pipe(sourcemaps.init());
			}

			var ret = ret
			      .pipe(sass(configStyles.sass).on('error', sass.logError));

			ret = ret
			  .pipe(postcss([ autoprefixer({ browsers: configStyles.autoprefixer.browsers }) ]));

			if (configStyles.sourceMaps) {
			  ret = ret
			    .pipe(sourcemaps.write(/*'.'*/null, { sourceRoot: configStyles.sourceMapsRoot }));	//saving sourcemaps to an external file causes Browsersync to reload whole page on SASS change
			}

			ret = ret
			  .pipe(gulp.dest(dirs.dist.styles));

		  return ret;
		}
	}
}<|endoftext|>


<commit_before>function Player(name, socket) {
  this.name = name;
  this.socket = socket;
  this.drawPile = [];
  this.discardPile = [];
  for(var i = 0; i < 7; ++i)
    this.discardPile.push(cards.Copper);
  for(var i = 0; i < 3; ++i)
    this.discardPile.push(cards.Estate);

  this.hand = [];
  this.draw(5);

  this.actions = null;
  this.money = null;
  this.buys = null;
  this.played = null;

  this.onChoice = null; //TODO should it be a stack?
  this.afterTurn = null;
}

Player.prototype = {
  takeTurn: function(callback) {
    this.afterTurn = callback;
    this.actions = 1;
    this.money = 0;
    this.buys = 1;
    this.played = [];

    this.promptAction();
  },

  promptAction: function() {
    if(!this.actions) {
      this.promptBuys();
      return;
    }

    this.sendStatus();

    var actionCards = this.hand.filter(function(c){return c.kind=="action";});
    var choices = actionCards.map(function(c){return c.name;});

    if (!choices.length) {
      this.send({message:"No actions to play"});
      this.promptBuys();
      return;
    }

    choices.push("Done With Actions");

    this.onChoice = this.receiveAction;
    this.send({choices:choices});
  },

  receiveAction: function(choice) {
    this.onChoice = null;

    if(choice == "Done With Actions") {
      this.promptBuys();
      return;
    }

    this.playCard(choice);
    --this.actions;

    this.promptAction();
  },

  promptBuys: function() {
    if(!this.buys) {
      this.turnDone();
      return;
    }

    this.sendStatus();

    var treasureCards = this.hand.filter(function(c){return c.kind=="treasure";});

    var choices = treasureCards.map(function(c){return c.name;});

    if (choices.length) {
      choices.unshift("Play All Treasures");
      choices.push("\n");
    }

    Array.prototype.push.apply(choices,
      game.store.getAvailable(this.money).map(function(c){return "Buy: " + c.name}));

    if (!choices.length) {
      this.send({message:"Nothing to buy"});
      this.turnDone();
      return;
    }

    choices.push("Done With Buys");

    this.onChoice = this.receiveBuys;
    this.send({choices:choices});
  },

  receiveBuys: function(choice) {
    var t = this;
    this.onChoice = null;

    if (choice == "Done With Buys") {
      this.turnDone();
      return;
    }

    if (choice == "Play All Treasures") {
      this.hand
        .filter(function(c){return c.kind=="treasure";})
        .forEach(function(c){t.playCard(c.name)});
      this.promptBuys();
      return;
    }

    if (choice.substring(0, 5) == "Buy: ") {
      var buying = cards[choice.substring(5)];
      this.discardPile.push(buying);
      this.money -= buying.cost;
      --this.buys;

      game.store.bought(buying);
    } else {
      this.playCard(choice);
    }

    this.promptBuys();
  },

  fromHand: function(name) {
    var hi = this.hand.map(function(c){return c.name;}).indexOf(name);
    if (hi == -1)
      return null;
    return this.hand.splice(hi, 1)[0];
  },

  playCard: function(name) {
    var card = this.fromHand(name);
    if (card == null) {
      console.error("Card doesn't exist: " + name);
      return;
    }
    card.play(this); //TODO will need to be async to handle cards with user interaction
    this.played.push(card);
  },

  sendStatus: function() {
    var message = "Hand: " + this.hand.map(function(c){return c.name}).toString();
    message += "\r\n";

    message += "Actions: " + this.actions;
    message += " Money: " + this.money;
    message += " Buys: " + this.buys;

    this.send({message: message});
  },

  turnDone: function() {
    Array.prototype.push.apply(this.discardPile, this.hand.splice(0));
    Array.prototype.push.apply(this.discardPile, this.played.splice(0));
    this.draw(5);

    this.send({message: ""});
    this.sendStatus();

    this.afterTurn();
  },

  draw: function(n) {
    for(var i = 0; i < (n || 1); ++i) {
      if(!this.drawPile.length) {
        this.shuffle();
        if(!this.drawPile.length)
          return;
      }
      this.hand.push(this.drawPile.pop());
    }
  },

  shuffle: function() {
    if(this.drawPile.length)
      console.error("drawPile isn't empty!");

    var array = this.discardPile;

    // Fisher-Yates (aka Knuth) Shuffle.
    var currentIndex = array.length, temporaryValue, randomIndex ;
    while (0 !== currentIndex) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }

    this.drawPile = this.discardPile;
    this.discardPile = [];
  },

  getPoints: function() {
    return this.drawPile.concat(this.discardPile).concat(this.hand).reduce(
      function(a, c) { return a + (c.points || 0); }, 0);
  },

  send: function(o) {
    this.socket.send(JSON.stringify(o));
  }
}

// Loudly fail so nobody can try-catch these errors
for(var name in Player.prototype)
  Player.prototype[name] = util.wrapErrors(Player.prototype[name]);
<commit_msg>sendChoice with callback<commit_after>function Player(name, socket) {
  this.name = name;
  this.socket = socket;
  this.drawPile = [];
  this.discardPile = [];
  for(var i = 0; i < 7; ++i)
    this.discardPile.push(cards.Copper);
  for(var i = 0; i < 3; ++i)
    this.discardPile.push(cards.Estate);

  this.hand = [];
  this.draw(5);

  this.actions = null;
  this.money = null;
  this.buys = null;
  this.played = null;

  this.onChoice = null; //TODO should it be a stack?
  this.afterTurn = null;
}

Player.prototype = {
  takeTurn: function(callback) {
    this.afterTurn = callback;
    this.actions = 1;
    this.money = 0;
    this.buys = 1;
    this.played = [];

    this.promptAction();
  },

  promptAction: function() {
    if(!this.actions) {
      this.promptBuys();
      return;
    }

    this.sendStatus();

    var actionCards = this.hand.filter(function(c){return c.kind=="action";});
    var choices = actionCards.map(function(c){return c.name;});

    if (!choices.length) {
      this.send({message:"No actions to play"});
      this.promptBuys();
      return;
    }

    choices.push("Done With Actions");

    this.sendChoice(choices, this.receiveAction);
  },

  receiveAction: function(choice) {
    if(choice == "Done With Actions") {
      this.promptBuys();
      return;
    }

    this.playCard(choice);
    --this.actions;

    this.promptAction();
  },

  promptBuys: function() {
    if(!this.buys) {
      this.turnDone();
      return;
    }

    this.sendStatus();

    var treasureCards = this.hand.filter(function(c){return c.kind=="treasure";});

    var choices = treasureCards.map(function(c){return c.name;});

    if (choices.length) {
      choices.unshift("Play All Treasures");
      choices.push("\n");
    }

    Array.prototype.push.apply(choices,
      game.store.getAvailable(this.money).map(function(c){return "Buy: " + c.name}));

    if (!choices.length) {
      this.send({message:"Nothing to buy"});
      this.turnDone();
      return;
    }

    choices.push("Done With Buys");

    this.sendChoice(choices, this.receiveBuys);
  },

  receiveBuys: function(choice) {
    var t = this;

    if (choice == "Done With Buys") {
      this.turnDone();
      return;
    }

    if (choice == "Play All Treasures") {
      this.hand
        .filter(function(c){return c.kind=="treasure";})
        .forEach(function(c){t.playCard(c.name)});
      this.promptBuys();
      return;
    }

    if (choice.substring(0, 5) == "Buy: ") {
      var buying = cards[choice.substring(5)];
      this.discardPile.push(buying);
      this.money -= buying.cost;
      --this.buys;

      game.store.bought(buying);
    } else {
      this.playCard(choice);
    }

    this.promptBuys();
  },

  fromHand: function(name) {
    var hi = this.hand.map(function(c){return c.name;}).indexOf(name);
    if (hi == -1)
      return null;
    return this.hand.splice(hi, 1)[0];
  },

  playCard: function(name) {
    var card = this.fromHand(name);
    if (card == null) {
      console.error("Card doesn't exist: " + name);
      return;
    }
    card.play(this); //TODO will need to be async to handle cards with user interaction
    this.played.push(card);
  },

  sendStatus: function() {
    var message = "Hand: " + this.hand.map(function(c){return c.name}).toString();
    message += "\r\n";

    message += "Actions: " + this.actions;
    message += " Money: " + this.money;
    message += " Buys: " + this.buys;

    this.send({message: message});
  },

  turnDone: function() {
    Array.prototype.push.apply(this.discardPile, this.hand.splice(0));
    Array.prototype.push.apply(this.discardPile, this.played.splice(0));
    this.draw(5);

    this.send({message: ""});
    this.sendStatus();

    this.afterTurn();
  },

  draw: function(n) {
    for(var i = 0; i < (n || 1); ++i) {
      if(!this.drawPile.length) {
        this.shuffle();
        if(!this.drawPile.length)
          return;
      }
      this.hand.push(this.drawPile.pop());
    }
  },

  shuffle: function() {
    if(this.drawPile.length)
      console.error("drawPile isn't empty!");

    var array = this.discardPile;

    // Fisher-Yates (aka Knuth) Shuffle.
    var currentIndex = array.length, temporaryValue, randomIndex ;
    while (0 !== currentIndex) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }

    this.drawPile = this.discardPile;
    this.discardPile = [];
  },

  getPoints: function() {
    return this.drawPile.concat(this.discardPile).concat(this.hand).reduce(
      function(a, c) { return a + (c.points || 0); }, 0);
  },

  send: function(o) {
    this.socket.send(JSON.stringify(o));
  },

  sendChoice: function(choices, handleChoice) {
    var t = this;
    this.onChoice = function(choice) {
      t.onChoice = null;
      handleChoice.call(t, choice);
    };
    this.socket.send(JSON.stringify({choices:choices}));
  }
}

// Loudly fail so nobody can try-catch these errors
for(var name in Player.prototype)
  Player.prototype[name] = util.wrapErrors(Player.prototype[name]);
<|endoftext|>


<commit_before>// Main cryptalk module
define({
	compiles: ['$'],
	requires: ['castrato','console','host','client']
}, function ($, requires, data) {

	// Require shortcut
	var mediator = requires.castrato;

	// Route mediator messages
	mediator
		.on('window:focused', function() {
			mediator.emit('audio:off');
			mediator.emit('notification:off');
		})
		.on('window:blurred',function() {
			mediator.emit('audio:on');
			mediator.emit('notification:on');
		})
		.on('command:mute', function () {
			mediator.emit('audio:mute');
		})
		.on('command:unmute', function () {
			mediator.emit('audio:unmute');
		})

		// Help console and host keep track of current states
		.on('room:changed', function(room) {
			mediator
				.emit('console:param', {
					room: room
				})
				.emit('host:param', {
					room: room
				});
		})
		.on('nick:changed', function(nick) {
			mediator.emit('console:param', {
				nick: nick
			});
		})
		.on('key:changed', function(key) {
			mediator
				.emit('console:param', {
					key: key
				})
				.emit('host:param', {
					key: key
				});
		})

		// Connect to the default host
		.emit('command:connect', undefined, function() {
			// Join room and set key if a hash in the format #Room:Key has been provided
			if ((hash = window.location.hash)) {
				parts = hash.slice(1).split(':');

				if (parts[0]) {
					mediator.emit('command:join', parts[0]);
				}

				if (parts[1]) {
					mediator.emit('command:key', parts[1]);
				}
			}
		});
});<commit_msg>Delete cryptalk.js<commit_after><|endoftext|>


<commit_before>angular.module( 'gj.Game' ).factory( 'Game', function( $state, $injector, App, Model, Environment, User, MediaItem )
{
	if ( $injector.has( 'Registry' ) ) {
		$injector.get( 'Registry' ).setConfig( 'Game', {
			maxItems: 50,
		} );
	}

	function Game( data )
	{
		if ( data ) {
			angular.extend( this, data );

			if ( data.developer && angular.isObject( data.developer ) ) {
				this.developer = new User( data.developer );
			}

			if ( data.thumbnail_media_item && angular.isObject( data.thumbnail_media_item ) ) {
				this.thumbnail_media_item = new MediaItem( data.thumbnail_media_item );
			}

			if ( data.header_media_item && angular.isObject( data.header_media_item ) ) {
				this.header_media_item = new MediaItem( data.header_media_item );
			}
		}

		this._has_cover = !!this.img_header;

		this.is_published = false;
		if ( this.status == Game.STATUS_VISIBLE ) {
			this.is_published = true;
		}

		this._is_finished = this.development_status == Game.DEVELOPMENT_STATUS_FINISHED;
		this._is_wip = this.development_status == Game.DEVELOPMENT_STATUS_WIP;
		this._is_devlog = this.development_status == Game.DEVELOPMENT_STATUS_DEVLOG;
		this._is_canceled = this.development_status == Game.DEVELOPMENT_STATUS_CANCELED;

		this._has_packages = false;
		if ( this.compatibility ) {
			var keys = Object.keys( this.compatibility );
			for ( var i in keys ) {
				if ( keys[ i ] != 'id' && keys[ i ] != 'game_id' ) {
					this._has_packages = true;
					break;
				}
			}
		}

		// We don't want to show ads if this game has sellable items.
		this._should_show_ads = true;
		if ( !this.ads_enabled ) {
			this._should_show_ads = false;
		}
		else if ( this.sellable && this.sellable.type != 'free' ) {
			this._should_show_ads = false;
		}

		// Should show as owned for the dev of the game.
		if (
			this.sellable
			&& this.sellable.type != 'free'
			&& App.user
			&& this.developer
			&& App.user.id == this.developer.id
		) {
			this.sellable.is_owned = true;
		}

		this._can_buy_primary_sellable = false;
		if ( this.sellable && this.sellable.type == 'paid' && !this.sellable.is_owned ) {
			this._can_buy_primary_sellable = true;
		}

		if ( $injector.has( 'Registry' ) ) {
			$injector.get( 'Registry' ).store( 'Game', this );
		}
	}

	Game.STATUS_HIDDEN = 0;
	Game.STATUS_VISIBLE = 1;
	Game.STATUS_REMOVED = 2;

	Game.DEVELOPMENT_STATUS_FINISHED = 1;
	Game.DEVELOPMENT_STATUS_WIP = 2;
	Game.DEVELOPMENT_STATUS_CANCELED = 3;
	Game.DEVELOPMENT_STATUS_DEVLOG = 4;

	Game.prototype.getSref = function( page, includeParams )
	{
		var sref = '';

		if ( page == 'dashboard' ) {
			sref = 'dashboard.developer.games.manage.game.overview';
		}
		else if ( page == 'edit' ) {
			sref = 'dashboard.developer.games.manage.game.details';
		}
		else {
			sref = 'discover.games.view.overview';
		}

		if ( includeParams ) {
			sref += '( ' + angular.toJson( this.getSrefParams( page ) ) + ' )'
		}

		return sref;
	};

	Game.prototype.getSrefParams = function( page )
	{
		if ( [ 'dashboard', 'edit' ].indexOf( page ) !== -1 ) {
			return { id: this.id };
		}

		return {
			id: this.id,
			category: this.category_slug,
			slug: this.slug
		};
	};

	Game.prototype.getUrl = function( page )
	{
		if ( page == 'soundtrack' ) {
			return '/games/' + this.slug + '/' + this.id + '/download/soundtrack';
		}

		return $state.href( this.getSref( page ), this.getSrefParams( page ) );
	};

	Game.prototype.hasDesktopSupport = function()
	{
		var compat = this.compatibility;
		return compat.os_windows
			|| compat.os_windows_64
			|| compat.os_mac
			|| compat.os_mac_64
			|| compat.os_linux
			|| compat.os_linux_64
			;
	};

	Game.prototype.hasBrowserSupport = function()
	{
		var compat = this.compatibility;
		return compat.type_html
			|| compat.type_flash
			|| compat.type_unity
			|| compat.type_applet
			|| compat.type_silverlight
			;
	};

	/**
	 * Helper function to check if the resource passed in has support for the
	 * os/arch passed in.
	 */
	Game.checkDeviceSupport = function( obj, os, arch )
	{
		if ( obj[ 'os_' + os ] ) {
			return true;
		}

		// If they are on 64bit, then we can check for 64bit only support as well.
		// If there is no arch (web site context) then we allow 64bit builds as well.
		if ( (!arch || arch == '64') && obj[ 'os_' + os + '_64' ] ) {
			return true;
		}

		return false;
	};

	Game.prototype.canInstall = function( os, arch )
	{
		// Obviously can't install if no desktop build.
		if ( !this.hasDesktopSupport() ) {
			return false;
		}

		return Game.checkDeviceSupport( this.compatibility, os, arch );
	};

	Game.pluckInstallableBuilds = function( packages, os, arch )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			// Don't include builds for packages that aren't bought yet.
			// Can't install them if they can't be bought.
			if (
				_package._sellable
				&& _package._sellable.type == 'paid'
				&& !_package._sellable.is_owned
			) {
				return;
			}

			_package._builds.forEach( function( build )
			{
				if ( Game.checkDeviceSupport( build, os, arch ) ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.pluckBrowserBuilds = function( packages )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			_package._builds.forEach( function( build )
			{
				if ( build.isBrowserBased() ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.pluckRomBuilds = function( packages )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			_package._builds.forEach( function( build )
			{
				if ( build.isRom() ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.chooseBestBuild = function( builds, os, arch )
	{
		var build, search;

		if ( arch == '64' ) {
			search = {};
			search[ 'os_' + os + '_64' ] = true;
			if ( build = _.find( builds, search ) ) {
				return build;
			}
		}

		search = {};
		search[ 'os_' + os ] = true;
		if ( build = _.find( builds, search ) ) {
			return build;
		}

		return builds[0];
	};

	Game.prototype.$save = function()
	{
		if ( this.id ) {
			return this.$_save( '/web/dash/developer/games/save/' + this.id, 'game' );
		}
		else {
			return this.$_save( '/web/dash/developer/games/save', 'game' );
		}
	};

	Game.prototype.$saveMaturity = function()
	{
		return this.$_save( '/web/dash/developer/games/maturity/save/' + this.id, 'game' );
	};

	Game.prototype.$saveThumbnail = function()
	{
		return this.$_save( '/web/dash/developer/games/thumbnail/save/' + this.id, 'game', { file: this.file, allowComplexData: [ 'crop' ] } );
	};

	Game.prototype.$saveHeader = function()
	{
		return this.$_save( '/web/dash/developer/games/header/save/' + this.id, 'game', { file: this.file } );
	};

	Game.prototype.$clearHeader = function()
	{
		return this.$_save( '/web/dash/developer/games/header/clear/' + this.id, 'game' );
	};

	Game.prototype.$setStatus = function( status )
	{
		return this.$_save( '/web/dash/developer/games/set-status/' + this.id + '/' + status, 'game' );
	};

	Game.prototype.$saveSettings = function()
	{
		return this.$_save( '/web/dash/developer/games/settings/save/' + this.id, 'game' );
	};

	Game.prototype.$remove = function( status )
	{
		return this.$_remove( '/web/dash/developer/games/remove/' + this.id );
	};

	return Model.create( Game );
} );
<commit_msg>img_header -> header_media_item<commit_after>angular.module( 'gj.Game' ).factory( 'Game', function( $state, $injector, App, Model, Environment, User, MediaItem )
{
	if ( $injector.has( 'Registry' ) ) {
		$injector.get( 'Registry' ).setConfig( 'Game', {
			maxItems: 50,
		} );
	}

	function Game( data )
	{
		if ( data ) {
			angular.extend( this, data );

			if ( data.developer && angular.isObject( data.developer ) ) {
				this.developer = new User( data.developer );
			}

			if ( data.thumbnail_media_item && angular.isObject( data.thumbnail_media_item ) ) {
				this.thumbnail_media_item = new MediaItem( data.thumbnail_media_item );
			}

			if ( data.header_media_item && angular.isObject( data.header_media_item ) ) {
				this.header_media_item = new MediaItem( data.header_media_item );
			}
		}

		this._has_cover = !!this.header_media_item;

		this.is_published = false;
		if ( this.status == Game.STATUS_VISIBLE ) {
			this.is_published = true;
		}

		this._is_finished = this.development_status == Game.DEVELOPMENT_STATUS_FINISHED;
		this._is_wip = this.development_status == Game.DEVELOPMENT_STATUS_WIP;
		this._is_devlog = this.development_status == Game.DEVELOPMENT_STATUS_DEVLOG;
		this._is_canceled = this.development_status == Game.DEVELOPMENT_STATUS_CANCELED;

		this._has_packages = false;
		if ( this.compatibility ) {
			var keys = Object.keys( this.compatibility );
			for ( var i in keys ) {
				if ( keys[ i ] != 'id' && keys[ i ] != 'game_id' ) {
					this._has_packages = true;
					break;
				}
			}
		}

		// We don't want to show ads if this game has sellable items.
		this._should_show_ads = true;
		if ( !this.ads_enabled ) {
			this._should_show_ads = false;
		}
		else if ( this.sellable && this.sellable.type != 'free' ) {
			this._should_show_ads = false;
		}

		// Should show as owned for the dev of the game.
		if (
			this.sellable
			&& this.sellable.type != 'free'
			&& App.user
			&& this.developer
			&& App.user.id == this.developer.id
		) {
			this.sellable.is_owned = true;
		}

		this._can_buy_primary_sellable = false;
		if ( this.sellable && this.sellable.type == 'paid' && !this.sellable.is_owned ) {
			this._can_buy_primary_sellable = true;
		}

		if ( $injector.has( 'Registry' ) ) {
			$injector.get( 'Registry' ).store( 'Game', this );
		}
	}

	Game.STATUS_HIDDEN = 0;
	Game.STATUS_VISIBLE = 1;
	Game.STATUS_REMOVED = 2;

	Game.DEVELOPMENT_STATUS_FINISHED = 1;
	Game.DEVELOPMENT_STATUS_WIP = 2;
	Game.DEVELOPMENT_STATUS_CANCELED = 3;
	Game.DEVELOPMENT_STATUS_DEVLOG = 4;

	Game.prototype.getSref = function( page, includeParams )
	{
		var sref = '';

		if ( page == 'dashboard' ) {
			sref = 'dashboard.developer.games.manage.game.overview';
		}
		else if ( page == 'edit' ) {
			sref = 'dashboard.developer.games.manage.game.details';
		}
		else {
			sref = 'discover.games.view.overview';
		}

		if ( includeParams ) {
			sref += '( ' + angular.toJson( this.getSrefParams( page ) ) + ' )'
		}

		return sref;
	};

	Game.prototype.getSrefParams = function( page )
	{
		if ( [ 'dashboard', 'edit' ].indexOf( page ) !== -1 ) {
			return { id: this.id };
		}

		return {
			id: this.id,
			category: this.category_slug,
			slug: this.slug
		};
	};

	Game.prototype.getUrl = function( page )
	{
		if ( page == 'soundtrack' ) {
			return '/games/' + this.slug + '/' + this.id + '/download/soundtrack';
		}

		return $state.href( this.getSref( page ), this.getSrefParams( page ) );
	};

	Game.prototype.hasDesktopSupport = function()
	{
		var compat = this.compatibility;
		return compat.os_windows
			|| compat.os_windows_64
			|| compat.os_mac
			|| compat.os_mac_64
			|| compat.os_linux
			|| compat.os_linux_64
			;
	};

	Game.prototype.hasBrowserSupport = function()
	{
		var compat = this.compatibility;
		return compat.type_html
			|| compat.type_flash
			|| compat.type_unity
			|| compat.type_applet
			|| compat.type_silverlight
			;
	};

	/**
	 * Helper function to check if the resource passed in has support for the
	 * os/arch passed in.
	 */
	Game.checkDeviceSupport = function( obj, os, arch )
	{
		if ( obj[ 'os_' + os ] ) {
			return true;
		}

		// If they are on 64bit, then we can check for 64bit only support as well.
		// If there is no arch (web site context) then we allow 64bit builds as well.
		if ( (!arch || arch == '64') && obj[ 'os_' + os + '_64' ] ) {
			return true;
		}

		return false;
	};

	Game.prototype.canInstall = function( os, arch )
	{
		// Obviously can't install if no desktop build.
		if ( !this.hasDesktopSupport() ) {
			return false;
		}

		return Game.checkDeviceSupport( this.compatibility, os, arch );
	};

	Game.pluckInstallableBuilds = function( packages, os, arch )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			// Don't include builds for packages that aren't bought yet.
			// Can't install them if they can't be bought.
			if (
				_package._sellable
				&& _package._sellable.type == 'paid'
				&& !_package._sellable.is_owned
			) {
				return;
			}

			_package._builds.forEach( function( build )
			{
				if ( Game.checkDeviceSupport( build, os, arch ) ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.pluckBrowserBuilds = function( packages )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			_package._builds.forEach( function( build )
			{
				if ( build.isBrowserBased() ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.pluckRomBuilds = function( packages )
	{
		var pluckedBuilds = [];

		packages.forEach( function( _package )
		{
			_package._builds.forEach( function( build )
			{
				if ( build.isRom() ) {
					pluckedBuilds.push( build );
				}
			} );
		} );

		return pluckedBuilds;
	};

	Game.chooseBestBuild = function( builds, os, arch )
	{
		var build, search;

		if ( arch == '64' ) {
			search = {};
			search[ 'os_' + os + '_64' ] = true;
			if ( build = _.find( builds, search ) ) {
				return build;
			}
		}

		search = {};
		search[ 'os_' + os ] = true;
		if ( build = _.find( builds, search ) ) {
			return build;
		}

		return builds[0];
	};

	Game.prototype.$save = function()
	{
		if ( this.id ) {
			return this.$_save( '/web/dash/developer/games/save/' + this.id, 'game' );
		}
		else {
			return this.$_save( '/web/dash/developer/games/save', 'game' );
		}
	};

	Game.prototype.$saveMaturity = function()
	{
		return this.$_save( '/web/dash/developer/games/maturity/save/' + this.id, 'game' );
	};

	Game.prototype.$saveThumbnail = function()
	{
		return this.$_save( '/web/dash/developer/games/thumbnail/save/' + this.id, 'game', { file: this.file, allowComplexData: [ 'crop' ] } );
	};

	Game.prototype.$saveHeader = function()
	{
		return this.$_save( '/web/dash/developer/games/header/save/' + this.id, 'game', { file: this.file } );
	};

	Game.prototype.$clearHeader = function()
	{
		return this.$_save( '/web/dash/developer/games/header/clear/' + this.id, 'game' );
	};

	Game.prototype.$setStatus = function( status )
	{
		return this.$_save( '/web/dash/developer/games/set-status/' + this.id + '/' + status, 'game' );
	};

	Game.prototype.$saveSettings = function()
	{
		return this.$_save( '/web/dash/developer/games/settings/save/' + this.id, 'game' );
	};

	Game.prototype.$remove = function( status )
	{
		return this.$_remove( '/web/dash/developer/games/remove/' + this.id );
	};

	return Model.create( Game );
} );
<|endoftext|>


<commit_before>const {Dispatcher} = require("flux");

const StreamingClient = require("@anyware/streaming-client");
const {SculptureStore, SculptureActionCreator} = require("@anyware/game-logic");

const PanelView = require('./views/panel-view');
const DiskView = require('./views/disk-view');

const SerialManager = require('./serial/serial-manager');

export default class SculptureApp {
  constructor(config) {
    this.config = config;

    this.dispatcher = new Dispatcher();
    this.dispatcher.register((payload) => {
      this._log(`Sent action: ${JSON.stringify(payload)}`);
    });

    this.client = null;

    this.panelView = null;
    this.diskView = null;

    const serialIdentity = this.config.HARDWARE_USERNAME_MAPPINGS[this.config.username];
    this.serialManager = new SerialManager(this.config, serialIdentity);
    this.serialManager.on(SerialManager.EVENT_COMMAND, (commandName, commandArgs) => {
      console.log(`COMMAND '${commandName}': ${JSON.stringify(commandArgs)}`);
    });

    this.sculpture = new SculptureStore(this.dispatcher, this.config);
    this.sculpture.on(SculptureStore.EVENT_CHANGE, (changes) => {
      this._log(`Sent state update: ${JSON.stringify(changes)}`);

      this.client.sendStateUpdate(changes);
    });

    this.sculptureActionCreator = new SculptureActionCreator(this.dispatcher);

    this._setupViews();
  }

  /**
   * Connects to the streaming server and sets up the rest of the application
   */
  connectAndSetup(options) {
    this._connectionOptions = options;
    this._setupStreamingClient();
  }

  _log(message) {
    console.log(message);
  }

  _error(message) {
    console.error(message);
  }

  _setupViews() {
    this.panelView = new PanelView(this.sculpture, this.dispatcher, this.serialManager);
    this.diskView = new DiskView(this.sculpture, this.dispatcher, this.serialManager);
  }

  _setupStreamingClient() {
    if (this.client) {
      this.client.close();
    }

    this._log(`Using username ${this._connectionOptions.username}`);

    this.client = new StreamingClient(this._connectionOptions);

    this.client.on(StreamingClient.EVENT_CONNECT, this._onConnectionStatusChange.bind(this));
    this.client.on(StreamingClient.EVENT_DISCONNECT, this._onConnectionStatusChange.bind(this));

    this.client.on(StreamingClient.EVENT_ERROR, this._error.bind(this));

    this.client.once(StreamingClient.EVENT_CONNECT, () => {
      //TODO: HACK! trying to compensate for the serial not connecting
      setTimeout(() => {
        //TODO: Temporarily here until the full game transitions are implemented
        //TODO: This if statement is here to account for reconnections
        if (this.sculpture.isPlayingNoGame) {
          const game = this.config.GAMES_SEQUENCE[0];
          this._log(`Starting ${game} game...`);
          this.sculptureActionCreator.sendStartGame(game);
        }
      }, 4000);
    });

    this.client.on(StreamingClient.EVENT_STATE_UPDATE, this._onStateUpdate.bind(this));
  }

  _onConnectionStatusChange() {
    this._log(`Client Connected: ${this.client.connected}`);
  }

  _onStateUpdate(update, metadata) {
    update.metadata = metadata;

    this._log(`Got state update: ${JSON.stringify(update)}`);

    this.sculptureActionCreator.sendMergeState(update);
  }
}

<commit_msg>Clarification of debug output<commit_after>const {Dispatcher} = require("flux");

const StreamingClient = require("@anyware/streaming-client");
const {SculptureStore, SculptureActionCreator} = require("@anyware/game-logic");

const PanelView = require('./views/panel-view');
const DiskView = require('./views/disk-view');

const SerialManager = require('./serial/serial-manager');

export default class SculptureApp {
  constructor(config) {
    this.config = config;

    this.dispatcher = new Dispatcher();
    this.dispatcher.register((payload) => {
      this._log(`Sent action: ${JSON.stringify(payload)}`);
    });

    this.client = null;

    this.panelView = null;
    this.diskView = null;

    const serialIdentity = this.config.HARDWARE_USERNAME_MAPPINGS[this.config.username];
    this.serialManager = new SerialManager(this.config, serialIdentity);
    this.serialManager.on(SerialManager.EVENT_COMMAND, (commandName, commandArgs) => {
      console.log(`COMMAND '${commandName}': ${JSON.stringify(commandArgs)}`);
    });

    this.sculpture = new SculptureStore(this.dispatcher, this.config);
    this.sculpture.on(SculptureStore.EVENT_CHANGE, (changes) => {
      this._log(`Sent state update: ${JSON.stringify(changes)}`);

      this.client.sendStateUpdate(changes);
    });

    this.sculptureActionCreator = new SculptureActionCreator(this.dispatcher);

    this._setupViews();
  }

  /**
   * Connects to the streaming server and sets up the rest of the application
   */
  connectAndSetup(options) {
    this._connectionOptions = options;
    this._setupStreamingClient();
  }

  _log(message) {
    console.log(message);
  }

  _error(message) {
    console.error(message);
  }

  _setupViews() {
    this.panelView = new PanelView(this.sculpture, this.dispatcher, this.serialManager);
    this.diskView = new DiskView(this.sculpture, this.dispatcher, this.serialManager);
  }

  _setupStreamingClient() {
    if (this.client) {
      this.client.close();
    }

    this._log(`Using username ${this._connectionOptions.username}`);

    this.client = new StreamingClient(this._connectionOptions);

    this.client.on(StreamingClient.EVENT_CONNECT, this._onConnectionStatusChange.bind(this));
    this.client.on(StreamingClient.EVENT_DISCONNECT, this._onConnectionStatusChange.bind(this));

    this.client.on(StreamingClient.EVENT_ERROR, this._error.bind(this));

    this.client.once(StreamingClient.EVENT_CONNECT, () => {
      //TODO: HACK! trying to compensate for the serial not connecting
      setTimeout(() => {
        //TODO: Temporarily here until the full game transitions are implemented
        //TODO: This if statement is here to account for reconnections
        if (this.sculpture.isPlayingNoGame) {
          const game = this.config.GAMES_SEQUENCE[0];
          this._log(`Starting ${game} game...`);
          this.sculptureActionCreator.sendStartGame(game);
        }
      }, 4000);
    });

    this.client.on(StreamingClient.EVENT_STATE_UPDATE, this._onStateUpdate.bind(this));
  }

  _onConnectionStatusChange() {
    this._log(`Streaming Client Connected: ${this.client.connected}`);
  }

  _onStateUpdate(update, metadata) {
    update.metadata = metadata;

    this._log(`Got state update: ${JSON.stringify(update)}`);

    this.sculptureActionCreator.sendMergeState(update);
  }
}

<|endoftext|>


<commit_before>module.exports = function (grunt) {
    'use strict';

    var path = require('path'),
        dirs = grunt.config('dirs'),
        files = grunt.config('files'),
        banner = grunt.config('banner'),
        minfiles = {};

    minfiles[path.join(dirs.build, files.minified)] = [path.join(dirs.build, files.main)];

    // minify es5 publish source (es6 minification is not supported)
    // include "banner" in the beginning
    grunt.config.merge({
        uglify: {
            options: {
                sourceMap: true,
                screwIE8: true,
                preserveComments: false,
                banner: banner,
                report: 'min',
                mangle: {
                    sort: true,
                    eval: true,
                    except: [
                        'mx',
                        'multiplex',
                        'Iterable',
                        'Iterator',
                        'ArrayIterable',
                        'ArrayIterator',
                        'ObjectIterable',
                        'ObjectIterator',
                        'EmptyIterable',
                        'GeneratorIterable',
                        'Collection',
                        'ReadOnlyCollection',
                        'List',
                        'SortedList',
                        'Dictionary',
                        'KeyValuePair',
                        'HashSet',
                        'HashTable',
                        'HashTableEntry',
                        'LinkedList',
                        'LinkedListNode',
                        'Queue',
                        'Stack',
                        'Lookup',
                        'Grouping',
                        'OrderedIterable',
                        'Comparer',
                        'EqualityComparer'
                    ]
                }
            },
            dist: {
                files: minfiles
            }
        }
    });

    return grunt;
};
<commit_msg>update uglify except list<commit_after>module.exports = function (grunt) {
    'use strict';

    var path = require('path'),
        dirs = grunt.config('dirs'),
        files = grunt.config('files'),
        banner = grunt.config('banner'),
        minfiles = {};

    minfiles[path.join(dirs.build, files.minified)] = [path.join(dirs.build, files.main)];

    // minify es5 publish source (es6 minification is not supported)
    // include "banner" in the beginning
    grunt.config.merge({
        uglify: {
            options: {
                sourceMap: true,
                screwIE8: true,
                preserveComments: false,
                banner: banner,
                report: 'min',
                mangle: {
                    sort: true,
                    eval: true,
                    except: [
                        'mx',
                        'multiplex',
                        'Iterable',
                        'Iterator',
                        'ArrayIterable',
                        'ArrayIterator',
                        'ObjectIterable',
                        'ObjectIterator',
                        'EmptyIterable',
                        'EmptyIterator',
                        'EnumerableIterator',
                        'Collection',
                        'ReadOnlyCollection',
                        'List',
                        'SortedList',
                        'Dictionary',
                        'KeyValuePair',
                        'HashSet',
                        'HashTable',
                        'HashTableEntry',
                        'LinkedList',
                        'LinkedListNode',
                        'Queue',
                        'Stack',
                        'Lookup',
                        'Grouping',
                        'OrderedIterable',
                        'Comparer',
                        'EqualityComparer'
                    ]
                }
            },
            dist: {
                files: minfiles
            }
        }
    });

    return grunt;
};
<|endoftext|>