<commit_before>(function() {
  function factory(ko, koext) {
    koext.dirtyFlag = function(root, isInitiallyDirty) {
      var _result = function () {},
          _initialState = ko.observable(ko.toJSON(root)),
          _isInitiallyDirty = ko.observable(isInitiallyDirty);

      _result.isDirty = ko.computed(function () {
        return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
      });

      _result.forceDirty =  function () {
        _isInitiallyDirty(true);
      };

      _result.reset = function () {
        _initialState(ko.toJSON(root));
        _isInitiallyDirty(false);
      };

      return _result;
    };

    koext.observableDate = function (initialValue) {
      var _actual = ko.observable(initialValue);

      var _day = ko.observable(),
          _month = ko.observable(),
          _year = ko.observable();

      function updateDate() {
        var day = _day(), month = _month(), year = _year();
        if (day !== undefined && month !== undefined && year !== undefined)
          _actual(new Date(year, month, day));
        else if (day === undefined && month === undefined && year === undefined)
          _actual(undefined);
      }

      _day.subscribe(updateDate);
      _month.subscribe(updateDate);
      _year.subscribe(updateDate);

      var result = ko.dependentObservable({
        read: function () {
          return _actual();
        },
        write: function (newValue) {
          var parsedValue = new Date(Date.parse(newValue));
          if (isNaN(parsedValue.valueOf())) {
            _actual(newValue);
            _day(undefined);
            _month(undefined);
            _year(undefined);
          }
          else {
            _day(parsedValue.getDate());
            _month(parsedValue.getMonth());
            _year(parsedValue.getFullYear());
            _actual(parsedValue);
          }
        }
      });

      result.day = _day;
      result.month = _month;
      result.year = _year;

      return result;
    };

    koext.observableInteger = function(initialValue) {
      var _actual = ko.observable(initialValue);

      var result = ko.dependentObservable({
        read: function() {
          return _actual();
        },
        write: function(newValue) {
          var parsedValue = parseInt(newValue);
          _actual(isNaN(parsedValue) ? newValue : parsedValue);
        }
      });

      return result;
    };

    koext.observableNumber = function(initialValue) {
      var _actual = ko.observable(initialValue);

      var result = ko.dependentObservable({
        read: function() {
          return _actual();
        },
        write: function(newValue) {
          var parsedValue = parseFloat(newValue);
          _actual(isNaN(parsedValue) ? newValue : parsedValue);
        }
      });

      return result;
    };

    koext.observableModel = function (model, initialValue) {
      var _value = initialValue || {};
      var _fields = {};

      function hasError(l) {
        return function (e) {
          return l.indexOf(e) !== -1;
        };
      }

      function cleanError(e) {
        return function () {
          e.removeAll();
        };
      }

      function errorParser(model, fields) {
        return function parseErrors(errors) {
          errors = errors || {};
          for (var p in model) {
            if (model.hasOwnProperty(p)) {
              fields[p].errors.removeAll();
              if (errors.hasOwnProperty(p)) {
                for (var e in errors[p]) {
                  if (errors[p].hasOwnProperty(e)) {
                    fields[p].errors.push(e);
                  }
                }
              }
            }
          }
        };
      }

      for (var p in model) {
        if (model.hasOwnProperty(p)) {
          if (typeof model[p] === 'object')
            _fields[p] = koext.observableModel(model[p], _value[p]);
          else if (model[p] === 'date')
            _fields[p] = koext.observableDate(_value[p]);
          else if (model[p] === 'number')
            _fields[p] = koext.observableNumber(_value[p]);
          else if (model[p] === 'integer')
            _fields[p] = koext.observableInteger(_value[p]);
          else
            _fields[p] = ko.observable(_value[p]);
          _fields[p].errors = ko.observableArray();
          _fields[p].errors.has = hasError(_fields[p].errors);
          _fields[p].subscribe(cleanError(_fields[p].errors));
        }
      }

      var result = ko.computed({
        read: function() {
          var obj = {}, empty = true;
          for (var p in model) {
            if (model.hasOwnProperty(p) && _fields.hasOwnProperty(p)) {
              obj[p] = _fields[p]();
              if (obj[p] !== undefined)
                empty = false;
            }
          }
          if (!empty)
            return obj;
        },
        write: function (newValue) {
          for (var p in model) {
            if (model.hasOwnProperty(p)) {
              if (newValue && newValue.hasOwnProperty(p))
                _fields[p](newValue[p]);
              else
                _fields[p](undefined);
            }
          }
        }
      });

      for (p in model)
        if (model.hasOwnProperty(p))
          result[p] = _fields[p];

      result.parseErrors = errorParser(model, _fields);
      result.dirtyFlag = koext.dirtyFlag(_fields);
      result.resetDirty = function () {
        result.dirtyFlag.reset();
        for (var p in model)
          if (model.hasOwnProperty(p) && typeof model[p] == 'object')
            _fields[p].resetDirty();
      };

      return result;
    };

    function processFiles(bindings, files) {
      var reader;

      if (bindings.fileDataURL && ko.isObservable(bindings.fileDataURL)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileDataURL(e.target.result);
          };
          reader.readAsDataURL(files[0]);
        } else
          bindings.fileDataURL(undefined);
      }

      if (bindings.fileArrayBuffer && ko.isObservable(bindings.fileArrayBuffer)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileArrayBuffer(e.target.result);
          };
          reader.readAsArrayBuffer(files[0]);
        } else
          bindings.fileArrayBuffer(undefined);
      }

      if (bindings.fileBinaryString && ko.isObservable(bindings.fileBinaryString)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileBinaryString(e.target.result);
          };
          reader.readAsBinaryString(files[0]);
        } else
          bindings.fileBinaryString(undefined);
      }

      if (bindings.fileText && ko.isObservable(bindings.fileText)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileText(e.target.result);
          };
          reader.readAsText(files[0]);
        } else
          bindings.fileText(undefined);
      }
    }

    ko.bindingHandlers.chooseFile = {
      init: function (element, valueAccessor, allBindingsAccesor) {
        var bindings = allBindingsAccesor();
        var form = document.createElement('form');
        var input = document.createElement('input');
        input.type = 'file';
        input.name = 'file';
        if (bindings.fileAccept)
          input.accept = ko.utils.unwrapObservable(bindings.fileAccept);
        if (bindings.fileMultiple && ko.utils.unwrapObservable(bindings.fileMultiple))
          input.multiple = "multiple";
        form.appendChild(input);
        element.onclick = function () {
          input.click();
        };
        input.onchange = function () {
          var value = valueAccessor();

          if (ko.isObservable(value))
            value(input.files);
          else
            processFiles(bindings, input.files);
        };
      },

      update: function (element, valueAccessor, allBindingsAccesor) {
        processFiles(allBindingsAccesor(), ko.utils.unwrapObservable(valueAccessor()));
      }
    };

    ko.subscribable.fn.subscribeChanged = function(callback) {
        if (!this.previousValueSubscription) {
            this.previousValueSubscription = this.subscribe(function(_previousValue) {
                this.previousValue = _previousValue;
            }, this, 'beforeChange');
        }
        return this.subscribe(function(latestValue) {
            callback(latestValue, this.previousValue);
        }, this);
    };

  } // factory

  if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
    factory(require("knockout"), exports);
  else if (typeof define === "function" && define.amd)
    define(["knockout", "exports"], factory);
  else
    factory(ko, ko);
})();
<commit_msg>Added returnKey binding<commit_after>(function() {
  function factory(ko, koext) {
    koext.dirtyFlag = function(root, isInitiallyDirty) {
      var _result = function () {},
          _initialState = ko.observable(ko.toJSON(root)),
          _isInitiallyDirty = ko.observable(isInitiallyDirty);

      _result.isDirty = ko.computed(function () {
        return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
      });

      _result.forceDirty =  function () {
        _isInitiallyDirty(true);
      };

      _result.reset = function () {
        _initialState(ko.toJSON(root));
        _isInitiallyDirty(false);
      };

      return _result;
    };

    koext.observableDate = function (initialValue) {
      var _actual = ko.observable(initialValue);

      var _day = ko.observable(),
          _month = ko.observable(),
          _year = ko.observable();

      function updateDate() {
        var day = _day(), month = _month(), year = _year();
        if (day !== undefined && month !== undefined && year !== undefined)
          _actual(new Date(year, month, day));
        else if (day === undefined && month === undefined && year === undefined)
          _actual(undefined);
      }

      _day.subscribe(updateDate);
      _month.subscribe(updateDate);
      _year.subscribe(updateDate);

      var result = ko.dependentObservable({
        read: function () {
          return _actual();
        },
        write: function (newValue) {
          var parsedValue = new Date(Date.parse(newValue));
          if (isNaN(parsedValue.valueOf())) {
            _actual(newValue);
            _day(undefined);
            _month(undefined);
            _year(undefined);
          }
          else {
            _day(parsedValue.getDate());
            _month(parsedValue.getMonth());
            _year(parsedValue.getFullYear());
            _actual(parsedValue);
          }
        }
      });

      result.day = _day;
      result.month = _month;
      result.year = _year;

      return result;
    };

    koext.observableInteger = function(initialValue) {
      var _actual = ko.observable(initialValue);

      var result = ko.dependentObservable({
        read: function() {
          return _actual();
        },
        write: function(newValue) {
          var parsedValue = parseInt(newValue);
          _actual(isNaN(parsedValue) ? newValue : parsedValue);
        }
      });

      return result;
    };

    koext.observableNumber = function(initialValue) {
      var _actual = ko.observable(initialValue);

      var result = ko.dependentObservable({
        read: function() {
          return _actual();
        },
        write: function(newValue) {
          var parsedValue = parseFloat(newValue);
          _actual(isNaN(parsedValue) ? newValue : parsedValue);
        }
      });

      return result;
    };

    koext.observableModel = function (model, initialValue) {
      var _value = initialValue || {};
      var _fields = {};

      function hasError(l) {
        return function (e) {
          return l.indexOf(e) !== -1;
        };
      }

      function cleanError(e) {
        return function () {
          e.removeAll();
        };
      }

      function errorParser(model, fields) {
        return function parseErrors(errors) {
          errors = errors || {};
          for (var p in model) {
            if (model.hasOwnProperty(p)) {
              fields[p].errors.removeAll();
              if (errors.hasOwnProperty(p)) {
                for (var e in errors[p]) {
                  if (errors[p].hasOwnProperty(e)) {
                    fields[p].errors.push(e);
                  }
                }
              }
            }
          }
        };
      }

      for (var p in model) {
        if (model.hasOwnProperty(p)) {
          if (typeof model[p] === 'object')
            _fields[p] = koext.observableModel(model[p], _value[p]);
          else if (model[p] === 'date')
            _fields[p] = koext.observableDate(_value[p]);
          else if (model[p] === 'number')
            _fields[p] = koext.observableNumber(_value[p]);
          else if (model[p] === 'integer')
            _fields[p] = koext.observableInteger(_value[p]);
          else
            _fields[p] = ko.observable(_value[p]);
          _fields[p].errors = ko.observableArray();
          _fields[p].errors.has = hasError(_fields[p].errors);
          _fields[p].subscribe(cleanError(_fields[p].errors));
        }
      }

      var result = ko.computed({
        read: function() {
          var obj = {}, empty = true;
          for (var p in model) {
            if (model.hasOwnProperty(p) && _fields.hasOwnProperty(p)) {
              obj[p] = _fields[p]();
              if (obj[p] !== undefined)
                empty = false;
            }
          }
          if (!empty)
            return obj;
        },
        write: function (newValue) {
          for (var p in model) {
            if (model.hasOwnProperty(p)) {
              if (newValue && newValue.hasOwnProperty(p))
                _fields[p](newValue[p]);
              else
                _fields[p](undefined);
            }
          }
        }
      });

      for (p in model)
        if (model.hasOwnProperty(p))
          result[p] = _fields[p];

      result.parseErrors = errorParser(model, _fields);
      result.dirtyFlag = koext.dirtyFlag(_fields);
      result.resetDirty = function () {
        result.dirtyFlag.reset();
        for (var p in model)
          if (model.hasOwnProperty(p) && typeof model[p] == 'object')
            _fields[p].resetDirty();
      };

      return result;
    };

    function processFiles(bindings, files) {
      var reader;

      if (bindings.fileDataURL && ko.isObservable(bindings.fileDataURL)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileDataURL(e.target.result);
          };
          reader.readAsDataURL(files[0]);
        } else
          bindings.fileDataURL(undefined);
      }

      if (bindings.fileArrayBuffer && ko.isObservable(bindings.fileArrayBuffer)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileArrayBuffer(e.target.result);
          };
          reader.readAsArrayBuffer(files[0]);
        } else
          bindings.fileArrayBuffer(undefined);
      }

      if (bindings.fileBinaryString && ko.isObservable(bindings.fileBinaryString)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileBinaryString(e.target.result);
          };
          reader.readAsBinaryString(files[0]);
        } else
          bindings.fileBinaryString(undefined);
      }

      if (bindings.fileText && ko.isObservable(bindings.fileText)) {
        if (files && files[0]) {
          reader = new FileReader();
          reader.onload = function (e) {
            bindings.fileText(e.target.result);
          };
          reader.readAsText(files[0]);
        } else
          bindings.fileText(undefined);
      }
    }

    ko.bindingHandlers.chooseFile = {
      init: function (element, valueAccessor, allBindingsAccesor) {
        var bindings = allBindingsAccesor();
        var form = document.createElement('form');
        var input = document.createElement('input');
        input.type = 'file';
        input.name = 'file';
        if (bindings.fileAccept)
          input.accept = ko.utils.unwrapObservable(bindings.fileAccept);
        if (bindings.fileMultiple && ko.utils.unwrapObservable(bindings.fileMultiple))
          input.multiple = "multiple";
        form.appendChild(input);
        element.onclick = function () {
          input.click();
        };
        input.onchange = function () {
          var value = valueAccessor();

          if (ko.isObservable(value))
            value(input.files);
          else
            processFiles(bindings, input.files);
        };
      },

      update: function (element, valueAccessor, allBindingsAccesor) {
        processFiles(allBindingsAccesor(), ko.utils.unwrapObservable(valueAccessor()));
      }
    };

    ko.subscribable.fn.subscribeChanged = function(callback) {
        if (!this.previousValueSubscription) {
            this.previousValueSubscription = this.subscribe(function(_previousValue) {
                this.previousValue = _previousValue;
            }, this, 'beforeChange');
        }
        return this.subscribe(function(latestValue) {
            callback(latestValue, this.previousValue);
        }, this);
    };

    ko.bindingHandlers.returnKey = {
      init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
        ko.utils.registerEventHandler(element, 'keydown', function(evt) {
          if (evt.keyCode === 13) {
            evt.preventDefault();
            evt.target.blur();
            valueAccessor().call(viewModel);
          }
        });
      }
    };

  } // factory

  if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
    factory(require("knockout"), exports);
  else if (typeof define === "function" && define.amd)
    define(["knockout", "exports"], factory);
  else
    factory(ko, ko);
})();
<|endoftext|>


<commit_before>

var Application = (function() {
    function Application(context) {
        var args = rectify(context),
            container = args['container'],
            canvas = args['canvas'],
            context = canvas.getContext('2d'),
            cellSize = args['cell-size'] || 15,
            width    = args['width'] || 26,
            height   = args['height'] || 20;

        this._container = container;
        this._canvas = canvas;
        this._context = context;

        this._eventHandler = new EventHander(this);
        this._display = new Display(this);
        this._game = new Game(this, width, height, cellSize);
    }

    function rectify(c) {
        var c = c || {},
            id = c['container-id'],
            dv = c['container'],
            dc = c['canvas'];

        if (id && dv && dc)
            return c;

        if (id && dv) {
            c['canvas'] = dv.getElementsByTagName('canvas')[0];
            return c;
        }

        if (id && dc) {
            c['container'] = document.getElementById(id);
            return c;
        }
        if (id) {
            dc = document.getElementById(id);
            c['canvas'] = dc.getElementsByTagName('canvas')[0];
            c['container'] = dc;
            return c;
        }

        throw new Error("Cannot rectify application arguments.");
    }

    function run() {
        this._game.run();
    }

    Application.prototype = PrototypeFactory.makeApplicationObject({
        run: run,
        registerEvent: function(type, fn) {
            this._eventHandler.register(type, fn);
            return this;
        },
        removeEvent: function(type, fn) {
            this._eventHandler.remove(type, fn);
            return this;
        },
        callEvent: function(type, args) {
            this._eventHandler.activate.apply(this, args);
            return this;
        }
    });

    return Application;
}());
<commit_msg>Turning application into an instance of evented object<commit_after>

var Application = (function() {
    function Application(context) {
        var args = rectify(context),
            container = args['container'],
            canvas = args['canvas'],
            context = canvas.getContext('2d'),
            cellSize = args['cell-size'] || 15,
            width    = args['width'] || 26,
            height   = args['height'] || 20;

        this._container = container;
        this._canvas = canvas;
        this._context = context;

        this._eventHandler = new EventHander(this);
        this._display = new Display(this);
        this._grid = new Grid(this, width, height, cellSize);
        this._game = new Game(this, this._display, this._grid);

        this.registerEvent('error', function() {
            console.log('foo');
            console.log(arguments);
        });
    }

    function rectify(c) {
        var c = c || {},
            id = c['container-id'],
            dv = c['container'],
            dc = c['canvas'];

        if (id && dv && dc)
            return c;

        if (id && dv) {
            c['canvas'] = dv.getElementsByTagName('canvas')[0];
            return c;
        }

        if (id && dc) {
            c['container'] = document.getElementById(id);
            return c;
        }
        if (id) {
            dc = document.getElementById(id);
            c['canvas'] = dc.getElementsByTagName('canvas')[0];
            c['container'] = dc;
            return c;
        }

        throw new Error("Cannot rectify application arguments.");
    }

    function run() {
        this._game.run();
    }

    Application.prototype = PrototypeFactory.makeEventedApplicationObject({
        run: run
    });

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


<commit_before>import { isString } from 'underscore';
import Property from './Property';

export default Property.extend({
  defaults: {
    ...Property.prototype.defaults,
    // 'background' is a good example where to make a difference
    // between detached and not
    //
    // - NOT detached (default)
    // background: url(..) no-repeat center ...;
    // - Detached
    // background-image: url();
    // background-repeat: repeat;
    // ...
    detached: 0,

    // Array of sub properties
    properties: [],

    // Separator to use to split property values (only for not detached properties)
    separator: ' ',

    // Separator to use to join property values (only for not detached properties)
    join: null,

    fromStyle: null,

    toStyle: null,
  },

  initialize(props = {}, opts = {}) {
    Property.callParentInit(Property, this, props, opts);
    const { em } = this;
    const Properties = require('./Properties').default;
    const properties = new Properties(this.get('properties') || [], { em, parentProp: this });
    this.set('properties', properties, { silent: 1 });
    this.listenTo(properties, 'change', this.__upProperties);
    this.listenTo(this, 'change:value', this.updateValues);
    Property.callInit(this, props, opts);
  },

  __upProperties(prop, opts = {}) {
    if (!this.__hasCustom() || opts.__up || opts.__clearIn) return;

    if (this.isDetached()) {
      const style = this.getProperties().reduce((acc, prop) => {
        acc[prop.getName()] = prop.hasValue({ noParent: true }) ? prop.__getFullValue() : '';
        return acc;
      }, {});
      this.__upTargetsStyle({ ...style, [prop.getName()]: prop.__getFullValue() }, opts);
    } else {
      const { __clear, ...rest } = opts;
      this.upValue(this.__getFullValue(), rest);
    }
  },

  __upTargetsStyle(style, opts) {
    const toStyle = this.get('toStyle');
    let newStyle = style;

    if (toStyle && !opts.__clear) {
      const values = this.getValues();
      newStyle = toStyle(values, { ...opts, style });
    }

    if (this.isDetached()) {
      newStyle[this.getName()] = '';
    } else {
      this.getProperties().map(prop => {
        newStyle[prop.getName()] = '';
      });
    }

    return Property.prototype.__upTargetsStyle.call(this, newStyle, opts);
  },

  __getFullValue(opts = {}) {
    if (this.isDetached() || opts.__clear) {
      return '';
    }

    return this.getProperties()
      .map(p => p.__getFullValue({ withDefault: 1 }))
      .filter(Boolean)
      .join(this.__getJoin());
  },

  __getJoin() {
    const join = this.get('join');
    return isString(join) ? join : this.get('separator');
  },

  __getFromStyle(style = {}) {
    let result = style;
    const fromStyle = this.get('fromStyle');

    if (fromStyle) {
      result = fromStyle(style);
    } else {
      const name = this.getName();
      const value = style[name];
      if (value) {
        const values = value.split(this.getSplitSeparator());
        this.getProperties().forEach((prop, i) => {
          const len = values.length;
          // Try to get value from a shorthand:
          // 11px -> 11px 11px 11px 11xp
          // 11px 22px -> 11px 22px 11px 22xp
          const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
          result[prop.getId()] = value || '';
        });
        result = {
          ...style,
          ...result,
        };
      }
    }

    return result;
  },

  clear() {
    this.getProperties().map(p => p.clear({ __clearIn: true }));
    return Property.prototype.clear.call(this);
  },

  /**
   * Get current values of properties
   * @param {Object} [opts={}] Options
   * @param {Boolean} [opts.byName=false] Use property name as key instead of ID
   * @returns {Object}
   */
  getValues({ byName } = {}) {
    return this.getProperties().reduce((res, prop) => {
      const key = byName ? prop.getName() : prop.getId();
      res[key] = `${prop.__getFullValue({ withDefault: 1 })}`;
      return res;
    }, {});
  },

  /**
   * Clear the value
   * @return {this}
   * @deprecated
   */
  clearValue(opts = {}) {
    this.get('properties').each(property => property.clearValue());
    return Property.prototype.clearValue.apply(this, arguments);
  },

  /**
   * Update property values
   * @deprecated
   */
  updateValues() {
    const values = this.getFullValue().split(this.getSplitSeparator());
    this.get('properties').each((property, i) => {
      const len = values.length;
      // Try to get value from a shorthand:
      // 11px -> 11px 11px 11px 11xp
      // 11px 22px -> 11px 22px 11px 22xp
      const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
      // There some issue with UndoManager
      //property.setValue(value, 0, {fromParent: 1});
    });
  },

  /**
   * Split by sperator but avoid it inside parenthesis
   * @return {RegExp}
   */
  getSplitSeparator() {
    return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`);
  },

  /**
   * Returns default value
   * @param  {Boolean} defaultProps Force to get defaults from properties
   * @return {string}
   */
  getDefaultValue(defaultProps) {
    let value = this.get('defaults');

    if (value && !defaultProps) {
      return value;
    }

    value = '';
    const properties = this.get('properties');
    properties.each((prop, index) => (value += `${prop.getDefaultValue()} `));
    return value.trim();
  },

  getFullValue() {
    if (this.get('detached')) {
      return '';
    }

    return this.get('properties').getFullValue();
  },

  /**
   * Get property at some index
   * @param  {Number} index
   * @return {Object}
   */
  getPropertyAt(index) {
    return this.get('properties').at(index);
  },

  isDetached() {
    return !!this.get('detached');
  },

  getProperties() {
    return [...this.get('properties').models];
  },

  getProperty(id) {
    return this.get('properties').filter(prop => prop.get('id') === id)[0] || null;
  },
});
<commit_msg>Update clear for detached properties<commit_after>import { isString } from 'underscore';
import Property from './Property';

export default Property.extend({
  defaults: {
    ...Property.prototype.defaults,
    // 'background' is a good example where to make a difference
    // between detached and not
    //
    // - NOT detached (default)
    // background: url(..) no-repeat center ...;
    // - Detached
    // background-image: url();
    // background-repeat: repeat;
    // ...
    detached: 0,

    // Array of sub properties
    properties: [],

    // Separator to use to split property values (only for not detached properties)
    separator: ' ',

    // Separator to use to join property values (only for not detached properties)
    join: null,

    fromStyle: null,

    toStyle: null,
  },

  initialize(props = {}, opts = {}) {
    Property.callParentInit(Property, this, props, opts);
    const { em } = this;
    const Properties = require('./Properties').default;
    const properties = new Properties(this.get('properties') || [], { em, parentProp: this });
    this.set('properties', properties, { silent: 1 });
    this.listenTo(properties, 'change', this.__upProperties);
    this.listenTo(this, 'change:value', this.updateValues);
    Property.callInit(this, props, opts);
  },

  __upProperties(prop, opts = {}) {
    if (!this.__hasCustom() || opts.__up || opts.__clearIn) return;

    if (this.isDetached()) {
      const style = this.getProperties().reduce((acc, prop) => {
        acc[prop.getName()] = prop.hasValue({ noParent: true }) ? prop.__getFullValue() : '';
        return acc;
      }, {});
      this.__upTargetsStyle({ ...style, [prop.getName()]: prop.__getFullValue() }, opts);
    } else {
      const { __clear, ...rest } = opts;
      this.upValue(this.__getFullValue(), rest);
    }
  },

  __upTargetsStyle(style, opts = {}) {
    const toStyle = this.get('toStyle');
    const { __clear } = opts;
    let newStyle = style;

    if (toStyle && !__clear) {
      const values = this.getValues();
      newStyle = toStyle(values, { ...opts, style });
    }

    if (this.isDetached()) {
      newStyle[this.getName()] = '';
    } else {
      this.getProperties().map(prop => {
        newStyle[prop.getName()] = '';
      });
    }

    return Property.prototype.__upTargetsStyle.call(this, newStyle, opts);
  },

  __getFullValue(opts = {}) {
    if (this.isDetached() || opts.__clear) {
      return '';
    }

    return this.getProperties()
      .map(p => p.__getFullValue({ withDefault: 1 }))
      .filter(Boolean)
      .join(this.__getJoin());
  },

  __getJoin() {
    const join = this.get('join');
    return isString(join) ? join : this.get('separator');
  },

  __getFromStyle(style = {}) {
    let result = style;
    const fromStyle = this.get('fromStyle');

    if (fromStyle) {
      result = fromStyle(style);
    } else {
      const name = this.getName();
      const value = style[name];
      if (value) {
        const values = value.split(this.getSplitSeparator());
        this.getProperties().forEach((prop, i) => {
          const len = values.length;
          // Try to get value from a shorthand:
          // 11px -> 11px 11px 11px 11xp
          // 11px 22px -> 11px 22px 11px 22xp
          const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
          result[prop.getId()] = value || '';
        });
        result = {
          ...style,
          ...result,
        };
      }
    }

    return result;
  },

  clear() {
    this.getProperties().map(p => p.clear({ __clearIn: !this.isDetached() }));
    return Property.prototype.clear.call(this);
  },

  hasValue(opts) {
    return this.getProperties().some(prop => prop.hasValue(opts));
  },

  /**
   * Get current values of properties
   * @param {Object} [opts={}] Options
   * @param {Boolean} [opts.byName=false] Use property name as key instead of ID
   * @returns {Object}
   */
  getValues({ byName } = {}) {
    return this.getProperties().reduce((res, prop) => {
      const key = byName ? prop.getName() : prop.getId();
      res[key] = `${prop.__getFullValue({ withDefault: 1 })}`;
      return res;
    }, {});
  },

  /**
   * Clear the value
   * @return {this}
   * @deprecated
   */
  clearValue(opts = {}) {
    this.get('properties').each(property => property.clearValue());
    return Property.prototype.clearValue.apply(this, arguments);
  },

  /**
   * Update property values
   * @deprecated
   */
  updateValues() {
    const values = this.getFullValue().split(this.getSplitSeparator());
    this.get('properties').each((property, i) => {
      const len = values.length;
      // Try to get value from a shorthand:
      // 11px -> 11px 11px 11px 11xp
      // 11px 22px -> 11px 22px 11px 22xp
      const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
      // There some issue with UndoManager
      //property.setValue(value, 0, {fromParent: 1});
    });
  },

  /**
   * Split by sperator but avoid it inside parenthesis
   * @return {RegExp}
   */
  getSplitSeparator() {
    return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`);
  },

  /**
   * Returns default value
   * @param  {Boolean} defaultProps Force to get defaults from properties
   * @return {string}
   */
  getDefaultValue(defaultProps) {
    let value = this.get('defaults');

    if (value && !defaultProps) {
      return value;
    }

    value = '';
    const properties = this.get('properties');
    properties.each((prop, index) => (value += `${prop.getDefaultValue()} `));
    return value.trim();
  },

  getFullValue() {
    if (this.get('detached')) {
      return '';
    }

    return this.get('properties').getFullValue();
  },

  /**
   * Get property at some index
   * @param  {Number} index
   * @return {Object}
   */
  getPropertyAt(index) {
    return this.get('properties').at(index);
  },

  isDetached() {
    return !!this.get('detached');
  },

  getProperties() {
    return [...this.get('properties').models];
  },

  getProperty(id) {
    return this.get('properties').filter(prop => prop.get('id') === id)[0] || null;
  },
});
<|endoftext|>


<commit_before>import Property from './Property';

export default Property.extend({
  defaults: {
    ...Property.prototype.defaults,
    // 'background' is a good example where to make a difference
    // between detached and not
    //
    // - NOT detached (default)
    // background: url(..) no-repeat center ...;
    // - Detached
    // background-image: url();
    // background-repeat: repeat;
    // ...
    detached: 0,

    // Array of sub properties
    properties: [],

    // Separator between properties
    separator: ' ',
  },

  initialize(props = {}, opts = {}) {
    Property.callParentInit(Property, this, props, opts);
    const { em } = this;
    const Properties = require('./Properties').default;
    const properties = new Properties(this.get('properties') || [], { em, parentProp: this });
    this.set('properties', properties, { silent: 1 });
    this.listenTo(properties, 'change', this.__upProperties);
    this.listenTo(this, 'change:value', this.updateValues);
    Property.callInit(this, props, opts);
  },

  __upProperties(prop, opts = {}) {
    if (!this.__hasCustom()) return;
    // if [detached] -> sm.addStyleTargets({ [prop.getName()]: prop.__getFullValue(), __p: !!opts.avoidStore }, opts)
    // else -> sm.addStyleTargets({ [this.getName()]: this.__getFullValue(), __p: !!opts.avoidStore }, opts)
  },

  __getFullValue() {
    if (this.get('detached')) return '';

    return this.getProperties()
      .map(p => p.__getFullValue())
      .join(this.get('separator'));
  },

  // TODO
  // in Property -> __upTargetsStyle({ [name]: value }, opt);

  /**
   * Clear the value
   * @return {this}
   */
  clearValue(opts = {}) {
    this.get('properties').each(property => property.clearValue());
    return Property.prototype.clearValue.apply(this, arguments);
  },

  /**
   * Update property values
   */
  updateValues() {
    const values = this.getFullValue().split(this.getSplitSeparator());
    this.get('properties').each((property, i) => {
      const len = values.length;
      // Try to get value from a shorthand:
      // 11px -> 11px 11px 11px 11xp
      // 11px 22px -> 11px 22px 11px 22xp
      const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
      // There some issue with UndoManager
      //property.setValue(value, 0, {fromParent: 1});
    });
  },

  /**
   * Split by sperator but avoid it inside parenthesis
   * @return {RegExp}
   */
  getSplitSeparator() {
    return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`);
  },

  /**
   * Returns default value
   * @param  {Boolean} defaultProps Force to get defaults from properties
   * @return {string}
   */
  getDefaultValue(defaultProps) {
    let value = this.get('defaults');

    if (value && !defaultProps) {
      return value;
    }

    value = '';
    const properties = this.get('properties');
    properties.each((prop, index) => (value += `${prop.getDefaultValue()} `));
    return value.trim();
  },

  getFullValue() {
    if (this.get('detached')) {
      return '';
    }

    return this.get('properties').getFullValue();
  },

  /**
   * Get property at some index
   * @param  {Number} index
   * @return {Object}
   */
  getPropertyAt(index) {
    return this.get('properties').at(index);
  },

  getProperties() {
    return [...this.get('properties').models];
  },

  getProperty(id) {
    return this.get('properties').filter(prop => prop.get('id') === id)[0] || null;
  },
});
<commit_msg>Add __upProperties in Composite<commit_after>import Property from './Property';

export default Property.extend({
  defaults: {
    ...Property.prototype.defaults,
    // 'background' is a good example where to make a difference
    // between detached and not
    //
    // - NOT detached (default)
    // background: url(..) no-repeat center ...;
    // - Detached
    // background-image: url();
    // background-repeat: repeat;
    // ...
    detached: 0,

    // Array of sub properties
    properties: [],

    // Separator between properties
    separator: ' ',
  },

  initialize(props = {}, opts = {}) {
    Property.callParentInit(Property, this, props, opts);
    const { em } = this;
    const Properties = require('./Properties').default;
    const properties = new Properties(this.get('properties') || [], { em, parentProp: this });
    this.set('properties', properties, { silent: 1 });
    this.listenTo(properties, 'change', this.__upProperties);
    this.listenTo(this, 'change:value', this.updateValues);
    Property.callInit(this, props, opts);
  },

  __upProperties(prop, opts = {}) {
    if (!this.__hasCustom()) return;

    if (this.get('detached')) {
      this.__upTargetsStyle({ [prop.getName()]: prop.__getFullValue() }, opts);
    } else {
      this.upValue(this.__getFullValue(), opts);
    }
  },

  __getFullValue() {
    if (this.get('detached')) return '';

    return this.getProperties()
      .map(p => p.__getFullValue())
      .join(this.get('separator'));
  },

  /**
   * Clear the value
   * @return {this}
   */
  clearValue(opts = {}) {
    this.get('properties').each(property => property.clearValue());
    return Property.prototype.clearValue.apply(this, arguments);
  },

  /**
   * Update property values
   */
  updateValues() {
    const values = this.getFullValue().split(this.getSplitSeparator());
    this.get('properties').each((property, i) => {
      const len = values.length;
      // Try to get value from a shorthand:
      // 11px -> 11px 11px 11px 11xp
      // 11px 22px -> 11px 22px 11px 22xp
      const value = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)];
      // There some issue with UndoManager
      //property.setValue(value, 0, {fromParent: 1});
    });
  },

  /**
   * Split by sperator but avoid it inside parenthesis
   * @return {RegExp}
   */
  getSplitSeparator() {
    return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`);
  },

  /**
   * Returns default value
   * @param  {Boolean} defaultProps Force to get defaults from properties
   * @return {string}
   */
  getDefaultValue(defaultProps) {
    let value = this.get('defaults');

    if (value && !defaultProps) {
      return value;
    }

    value = '';
    const properties = this.get('properties');
    properties.each((prop, index) => (value += `${prop.getDefaultValue()} `));
    return value.trim();
  },

  getFullValue() {
    if (this.get('detached')) {
      return '';
    }

    return this.get('properties').getFullValue();
  },

  /**
   * Get property at some index
   * @param  {Number} index
   * @return {Object}
   */
  getPropertyAt(index) {
    return this.get('properties').at(index);
  },

  getProperties() {
    return [...this.get('properties').models];
  },

  getProperty(id) {
    return this.get('properties').filter(prop => prop.get('id') === id)[0] || null;
  },
});
<|endoftext|>


<commit_before>"use strict";

module.exports = function(ecs, game) { // eslint-disable-line no-unused-vars
	ecs.add(function resolveCollisions() { // eslint-disable-line no-unused-vars
		var entitiesCollision = game.entities.find("collisions");
		var foundCollisions = false;
		var foundObstacle = false;
		for (var i = 0; i < entitiesCollision.length; i++) {
			var collisions = game.entities.get(entitiesCollision[i], "collisions");
			var obstacle = game.entities.get(4,)
			if (collisions.length > 0) {
				foundCollisions = true;
				break;
			}
			if ()
		}
		if (foundCollisions === true && foundObstacle === true) {
			var entitiesWithVelocity = game.entities.find("velocity");
			for (i = 0; i < entitiesWithVelocity.length; i++) {
				game.entities.remove(entitiesWithVelocity[i], "velocity");
			}
		}
	});
};
<commit_msg>Update resolve-collisions.js<commit_after>"use strict";

module.exports = function(ecs, game) { // eslint-disable-line no-unused-vars
	ecs.add(function resolveCollisions() { // eslint-disable-line no-unused-vars
		var entitiesCollision = game.entities.find("collisions");
		var foundCollisions = false;
		var foundObstacle = false;
		for (var i = 0; i < entitiesCollision.length; i++) {
			var collisions = game.entities.get(entitiesCollision[i], "collisions");
			var obstacle = game.entities.get(4,)
			if (collisions.length > 0) {
				foundCollisions = true;
				break;
			}
			if ()//I do not know what to put here
			// I tried game.entities.get but I think that needs an array.
		}
		if (foundCollisions === true && foundObstacle === true) { // to make this true
			var entitiesWithVelocity = game.entities.find("velocity");
			for (i = 0; i < entitiesWithVelocity.length; i++) {
				game.entities.remove(entitiesWithVelocity[i], "velocity");
			}
		}
	});
};
<|endoftext|>


<commit_before>const octokit = require(`@octokit/rest`);
const fixtureJsonStringify = require(`./fixture-json-stringify.js`);

require(`./load-env-file.js`);

/**
 * @typedef importPluginReturn
 * @type {!object}
 * @property {!object} manufacturers like in manufacturers.json
 * @property {!object} fixtures Object keys are of the form 'manufacturerKey/fixtureKey', the value is like in fixture JSON files.
 * @property {?string} submitter GitHub user name of the fixture submitter.
 */

/**
 * @param {!importPluginReturn} out The object like returned by an import plugin.
 * @returns {!Promise<!string, !Error>} A promise that resolves to the pull request URL, or rejects with an error.
 */
module.exports = function createPullRequest(out) {
  const repository = process.env.NODE_ENV === `production` ? `open-fixture-library` : `ofl-test`;

  const userToken = process.env.GITHUB_USER_TOKEN;
  if (userToken === undefined) {
    console.error(`.env file does not contain GITHUB_USER_TOKEN variable`);
    return Promise.reject(new Error(`GitHub user token was not set`));
  }

  let pullRequestUrl;

  const timestamp = new Date().toISOString().replace(/:/g, `-`).replace(/\..+/, ``);
  const branchName = `branch${timestamp}`;

  const changedFiles = [];
  const warnings = [];

  octokit.authenticate({
    type: `token`,
    token: userToken
  });

  console.log(`get latest commit hash ...`);
  return octokit.gitdata.getReference({
    owner: `OpenLightingProject`,
    repo: repository,
    ref: `heads/master`
  })
    .then(result => {
      const latestCommitHash = result.data.object.sha;
      console.log(latestCommitHash);

      console.log(`create new branch '${branchName}' ...`);
      return octokit.gitdata.createReference({
        owner: `OpenLightingProject`,
        repo: repository,
        ref: `refs/heads/${branchName}`,
        sha: latestCommitHash
      });
    })
    .then(() => {
      console.log(`done`);

      return addOrUpdateFile(`fixtures/manufacturers.json`, `manufacturers.json`, oldFileContent => {
        if (oldFileContent == null) {
          return prettyJsonStringify(out.manufacturers);
        }
        out.manufacturers = Object.assign({}, JSON.parse(oldFileContent), out.manufacturers);
        return prettyJsonStringify(out.manufacturers);
      });
    })
    .then(() => {
      let chain = Promise.resolve();

      for (const fixtureKey of Object.keys(out.fixtures)) {
        chain = chain.then(() => addOrUpdateFile(
          `fixtures/${fixtureKey}.json`, `fixture '${fixtureKey}'`,
          oldFileContent => fixtureJsonStringify(out.fixtures[fixtureKey])
        ));
      }

      return chain;
    })
    .then(() => addOrUpdateFile(`fixtures/register.json`, `register.json`, oldFileContent => {
      const register = (oldFileContent !== null) ? JSON.parse(oldFileContent) : {
        filesystem: {},
        manufacturers: {},
        categories: {},
        contributors: {},
        rdm: {}
      };

      // add new manufacturer RDM IDs to register
      for (const manKey of Object.keys(out.manufacturers)) {
        if (manKey !== `$schema` && `rdmId` in out.manufacturers[manKey] && !(out.manufacturers[manKey].rdmId in register.rdm)) {
          register.rdm[out.manufacturers[manKey].rdmId] = {
            key: manKey,
            models: {}
          };
        }
      }

      // add new fixtures to register
      for (const fixtureKey of Object.keys(out.fixtures)) {
        const [manKey, fixKey] = fixtureKey.split(`/`);
        const fixData = out.fixtures[fixtureKey];

        let lastAction = `modified`;
        if (fixData.meta.lastModifyDate === fixData.meta.createDate) {
          lastAction = `created`;
        }
        else if (`importPlugin` in fixData.meta && fixData.meta.lastModifyDate === fixData.meta.importPlugin) {
          lastAction = `imported`;
        }

        // add to filesystem register
        register.filesystem[fixtureKey] = {
          name: fixData.name,
          lastModifyDate: fixData.meta.lastModifyDate,
          lastAction: lastAction
        };

        // add to manufacturer register
        if (!(manKey in register.manufacturers)) {
          register.manufacturers[manKey] = [];
        }
        register.manufacturers[manKey].push(fixKey);

        // add to category register
        for (const cat of fixData.categories) {
          if (!(cat in register.categories)) {
            register.categories[cat] = [];
          }
          register.categories[cat].push(fixtureKey);
        }

        // add to contributor register
        for (const contributor of fixData.meta.authors) {
          if (!(contributor in register.contributors)) {
            register.contributors[contributor] = [];
          }
          register.contributors[contributor].push(fixtureKey);
        }

        // add to rdm register
        if (`rdm` in fixData) {
          register.rdm[out.manufacturers[manKey].rdmId].models[fixData.rdm.modelId] = fixKey;
        }
      }

      return prettyJsonStringify(getSortedRegister(register));
    }))
    .then(() => {
      console.log(`create pull request ...`);

      const addedFixtures = changedFiles.filter(line => line.startsWith(`Add fixture`));
      let title = `Add ${addedFixtures.length} new fixtures`;
      if (addedFixtures.length === 1) {
        title = addedFixtures[0];
      }

      let body = changedFiles.map(line => `* ${line}`).join(`\n`);

      if (warnings.length > 0) {
        body += `\n\n### Warnings:\n* ${warnings.join(`\n* `)}`;
      }

      const fixtureWarningsErrors = getFixtureWarningsErrorsMarkdownList();
      if (fixtureWarningsErrors.length) {
        body += `\n\n### Fixture warnings / errors\n\n`;
        body += fixtureWarningsErrors;
      }

      body += `\n\nThank you ${getSubmitterNameMarkdown()}!`;

      return octokit.pullRequests.create({
        owner: `OpenLightingProject`,
        repo: repository,
        title: title,
        body: body,
        head: branchName,
        base: `master`
      });
    })
    .then(result => {
      console.log(`done`);
      pullRequestUrl = result.data.html_url;

      console.log(`add labels to pull request ...`);
      return octokit.issues.addLabels({
        owner: `OpenLightingProject`,
        repo: repository,
        number: result.data.number,
        labels: [`new-fixture`, `via-editor`]
      });
    })
    .then(() => {
      console.log(`done`);
      console.log(`View the pull request at ${pullRequestUrl}`);
      return pullRequestUrl;
    })
    .catch(error => {
      console.error(`Error: ${error.message}`);
      return Promise.reject(error);
    });

  /**
   * @returns {!string} A Markdown list of all fixture warnings and errors.
   */
  function getFixtureWarningsErrorsMarkdownList() {
    let markdownList = ``;

    if (!(`errors` in out)) {
      out.errors = {};
    }
    if (!(`warnings` in out)) {
      out.warnings = {};
    }

    for (const fixtureKey of Object.keys(out.fixtures)) {
      const fixtureErrors = out.errors[fixtureKey] || [];
      const fixtureWarnings = out.warnings[fixtureKey] || [];

      const messages = fixtureErrors.map(
        error => `  - :x: ${error}`
      ).concat(fixtureWarnings.map(
        warning => `  - :warning: ${warning}`
      ));

      if (messages.length) {
        markdownList += `* ${fixtureKey}\n${messages.join(`\n`)}\n`;
      }
    }

    return markdownList;
  }

  /**
   * @returns {!string} The submitter of the fixture(s), or the first author of the first fixture.
   */
  function getSubmitterNameMarkdown() {
    if (out.submitter) {
      return /^[a-zA-Z0-9]+$/.test(out.submitter) ? `@${out.submitter}` : `**${out.submitter}**`;
    }

    const firstFixtureKey = Object.keys(out.fixtures)[0];
    const firstFixtureAuthor = out.fixtures[firstFixtureKey].meta.authors[0];

    return `**${firstFixtureAuthor}**`;
  }

  /**
   * @callback newContentFunction
   * @param {?string} oldFileContent The text content of the old file, or null if it has not existed yet.
   * @return {!string} The new file text content.
   */

  /**
   * @param {!string} filename The path of the file (relative to the repository root) to add or update.
   * @param {!string} displayName A shorter file name to display in commit messages and PR body.
   * @param {!newContentFunction} newContentFunction The callback to get the new file content from.
   * @returns {!Promise} Promise that is resolved when the file is successfully / erroneously updated / added.
   */
  function addOrUpdateFile(filename, displayName, newContentFunction) {
    console.log(`does ${displayName} exist?`);

    return octokit.repos.getContent({
      owner: `OpenLightingProject`,
      repo: repository,
      path: filename
    })
      .then(result => {
        console.log(`yes -> update it ...`);

        const oldFileContent = Buffer.from(result.data.content, result.data.encoding).toString(`utf8`);
        const newFileContent = newContentFunction(oldFileContent);

        if (oldFileContent === newFileContent) {
          console.log(`no need to update, files are the same`);
          return Promise.resolve();
        }

        return octokit.repos.updateFile({
          owner: `OpenLightingProject`,
          repo: repository,
          path: filename,
          message: `Update ${displayName} via editor`,
          content: encodeBase64(newFileContent),
          sha: result.data.sha,
          branch: branchName
        })
          .then(() => {
            console.log(`done`);
            changedFiles.push(`Update ${displayName}`);
          })
          .catch(error => {
            console.error(`Error updating ${displayName}: ${error.message}`);
            warnings.push(`* Error updating ${displayName}: \`${error.message}\``);
          });
      })
      .catch(error => {
        console.log(`no -> create it ...`);
        return octokit.repos.createFile({
          owner: `OpenLightingProject`,
          repo: repository,
          path: filename,
          message: `Add ${displayName} via editor`,
          content: encodeBase64(newContentFunction(null)),
          branch: branchName
        })
          .then(() => {
            console.log(`done`);
            changedFiles.push(`Add ${displayName}`);
          })
          .catch(error => {
            console.error(`Error adding ${displayName}: ${error.message}`);
            warnings.push(`* Error adding ${displayName}: \`${error.message}\``);
          });
      });
  }

  /**
   * @param {!object} register The register to sort.
   * @returns {!object} The sorted register.
   */
  function getSortedRegister(register) {
    const newRegister = {
      filesystem: {},
      manufacturers: {},
      categories: {},
      contributors: {},
      rdm: {}
    };

    // copy sorted filesystem into register
    for (const fixKey of Object.keys(register.filesystem).sort(localeSort)) {
      newRegister.filesystem[fixKey] = register.filesystem[fixKey];
    }

    // copy sorted manufacturers into register
    for (const man of Object.keys(register.manufacturers).sort(localeSort)) {
      newRegister.manufacturers[man] = register.manufacturers[man].sort(localeSort);
    }

    // copy sorted categories into register
    for (const cat of Object.keys(register.categories).sort(localeSort)) {
      newRegister.categories[cat] = register.categories[cat].sort(localeSort);
    }

    // copy sorted contributors into register
    const sortedContributors = Object.keys(register.contributors).sort((a, b) => {
      const fixturesDelta = register.contributors[b].length - register.contributors[a].length;
      const nameDelta = a > b ? 1 : a < b ? -1 : 0;
      return fixturesDelta !== 0 ? fixturesDelta : nameDelta;
    });
    for (const contributor of sortedContributors) {
      newRegister.contributors[contributor] = register.contributors[contributor].sort(localeSort);
    }

    // the higher the value, the higher the rank if the dates are the same
    const lastActionRankMapping = {
      'modified': 10,
      'imported': 20,
      'created': 30
    };

    // add fixture list sorted by lastModifyDate
    newRegister.lastUpdated = Object.keys(newRegister.filesystem).filter(
      fixtureKey => `lastModifyDate` in register.filesystem[fixtureKey]
    ).sort((a, b) => {
      const fixA = register.filesystem[a];
      const fixB = register.filesystem[b];
      const dateDelta = new Date(fixB.lastModifyDate) - new Date(fixA.lastModifyDate);
      const actionDelta = lastActionRankMapping[fixB.lastAction] - lastActionRankMapping[fixA.lastAction];
      const keyDelta = a > b ? 1 : a < b ? -1 : 0;
      return dateDelta !== 0 ? dateDelta : (actionDelta !== 0 ? actionDelta : keyDelta);
    });

    // copy sorted RDM data into register
    for (const manId of Object.keys(register.rdm).sort(localeSort)) {
      newRegister.rdm[manId] = {
        key: register.rdm[manId].key,
        models: {}
      };

      for (const fixId of Object.keys(register.rdm[manId].models).sort(localeSort)) {
        newRegister.rdm[manId].models[fixId] = register.rdm[manId].models[fixId];
      }
    }

    return newRegister;
  }
};

/**
 * @param {!string} string The string to encode in base 64.
 * @returns {!string} The encoded string.
 */
function encodeBase64(string) {
  return Buffer.from(string).toString(`base64`);
}

/**
 * JSON.stringify with an extra newline character at the end.
 * @param {!object} obj JSON object.
 * @returns {!string} String representing the given object.
 */
function prettyJsonStringify(obj) {
  const str = JSON.stringify(obj, null, 2);

  return `${str}\n`;
}

/**
 * Function to pass into Array.sort().
 * @param {!string} a The first string.
 * @param {!string} b The second string.
 * @returns {!number} A number indicating the order of the two strings.
 */
function localeSort(a, b) {
  return a.localeCompare(b, `en`, {
    numeric: true
  });
}
<commit_msg>Fix create-github-pr script (#597)<commit_after>const fixtureJsonStringify = require(`./fixture-json-stringify.js`);

require(`./load-env-file.js`);

const githubClient = new require(`@octokit/rest`)();

/**
 * @typedef importPluginReturn
 * @type {!object}
 * @property {!object} manufacturers like in manufacturers.json
 * @property {!object} fixtures Object keys are of the form 'manufacturerKey/fixtureKey', the value is like in fixture JSON files.
 * @property {?string} submitter GitHub user name of the fixture submitter.
 */

/**
 * @param {!importPluginReturn} out The object like returned by an import plugin.
 * @returns {!Promise<!string, !Error>} A promise that resolves to the pull request URL, or rejects with an error.
 */
module.exports = function createPullRequest(out) {
  const repository = process.env.NODE_ENV === `production` ? `open-fixture-library` : `ofl-test`;

  const userToken = process.env.GITHUB_USER_TOKEN;
  if (userToken === undefined) {
    console.error(`.env file does not contain GITHUB_USER_TOKEN variable`);
    return Promise.reject(new Error(`GitHub user token was not set`));
  }

  let pullRequestUrl;

  const timestamp = new Date().toISOString().replace(/:/g, `-`).replace(/\..+/, ``);
  const branchName = `branch${timestamp}`;

  const changedFiles = [];
  const warnings = [];

  githubClient.authenticate({
    type: `token`,
    token: userToken
  });

  console.log(`get latest commit hash ...`);
  return githubClient.gitdata.getReference({
    owner: `OpenLightingProject`,
    repo: repository,
    ref: `heads/master`
  })
    .then(result => {
      const latestCommitHash = result.data.object.sha;
      console.log(latestCommitHash);

      console.log(`create new branch '${branchName}' ...`);
      return githubClient.gitdata.createReference({
        owner: `OpenLightingProject`,
        repo: repository,
        ref: `refs/heads/${branchName}`,
        sha: latestCommitHash
      });
    })
    .then(() => {
      console.log(`done`);

      return addOrUpdateFile(`fixtures/manufacturers.json`, `manufacturers.json`, oldFileContent => {
        if (oldFileContent == null) {
          return prettyJsonStringify(out.manufacturers);
        }
        out.manufacturers = Object.assign({}, JSON.parse(oldFileContent), out.manufacturers);
        return prettyJsonStringify(out.manufacturers);
      });
    })
    .then(() => {
      let chain = Promise.resolve();

      for (const fixtureKey of Object.keys(out.fixtures)) {
        chain = chain.then(() => addOrUpdateFile(
          `fixtures/${fixtureKey}.json`, `fixture '${fixtureKey}'`,
          oldFileContent => fixtureJsonStringify(out.fixtures[fixtureKey])
        ));
      }

      return chain;
    })
    .then(() => addOrUpdateFile(`fixtures/register.json`, `register.json`, oldFileContent => {
      const register = (oldFileContent !== null) ? JSON.parse(oldFileContent) : {
        filesystem: {},
        manufacturers: {},
        categories: {},
        contributors: {},
        rdm: {}
      };

      // add new manufacturer RDM IDs to register
      for (const manKey of Object.keys(out.manufacturers)) {
        if (manKey !== `$schema` && `rdmId` in out.manufacturers[manKey] && !(out.manufacturers[manKey].rdmId in register.rdm)) {
          register.rdm[out.manufacturers[manKey].rdmId] = {
            key: manKey,
            models: {}
          };
        }
      }

      // add new fixtures to register
      for (const fixtureKey of Object.keys(out.fixtures)) {
        const [manKey, fixKey] = fixtureKey.split(`/`);
        const fixData = out.fixtures[fixtureKey];

        let lastAction = `modified`;
        if (fixData.meta.lastModifyDate === fixData.meta.createDate) {
          lastAction = `created`;
        }
        else if (`importPlugin` in fixData.meta && fixData.meta.lastModifyDate === fixData.meta.importPlugin) {
          lastAction = `imported`;
        }

        // add to filesystem register
        register.filesystem[fixtureKey] = {
          name: fixData.name,
          lastModifyDate: fixData.meta.lastModifyDate,
          lastAction: lastAction
        };

        // add to manufacturer register
        if (!(manKey in register.manufacturers)) {
          register.manufacturers[manKey] = [];
        }
        register.manufacturers[manKey].push(fixKey);

        // add to category register
        for (const cat of fixData.categories) {
          if (!(cat in register.categories)) {
            register.categories[cat] = [];
          }
          register.categories[cat].push(fixtureKey);
        }

        // add to contributor register
        for (const contributor of fixData.meta.authors) {
          if (!(contributor in register.contributors)) {
            register.contributors[contributor] = [];
          }
          register.contributors[contributor].push(fixtureKey);
        }

        // add to rdm register
        if (`rdm` in fixData) {
          register.rdm[out.manufacturers[manKey].rdmId].models[fixData.rdm.modelId] = fixKey;
        }
      }

      return prettyJsonStringify(getSortedRegister(register));
    }))
    .then(() => {
      console.log(`create pull request ...`);

      const addedFixtures = changedFiles.filter(line => line.startsWith(`Add fixture`));
      let title = `Add ${addedFixtures.length} new fixtures`;
      if (addedFixtures.length === 1) {
        title = addedFixtures[0];
      }

      let body = changedFiles.map(line => `* ${line}`).join(`\n`);

      if (warnings.length > 0) {
        body += `\n\n### Warnings:\n* ${warnings.join(`\n* `)}`;
      }

      const fixtureWarningsErrors = getFixtureWarningsErrorsMarkdownList();
      if (fixtureWarningsErrors.length) {
        body += `\n\n### Fixture warnings / errors\n\n`;
        body += fixtureWarningsErrors;
      }

      body += `\n\nThank you ${getSubmitterNameMarkdown()}!`;

      return githubClient.pullRequests.create({
        owner: `OpenLightingProject`,
        repo: repository,
        title: title,
        body: body,
        head: branchName,
        base: `master`
      });
    })
    .then(result => {
      console.log(`done`);
      pullRequestUrl = result.data.html_url;

      console.log(`add labels to pull request ...`);
      return githubClient.issues.addLabels({
        owner: `OpenLightingProject`,
        repo: repository,
        number: result.data.number,
        labels: [`new-fixture`, `via-editor`]
      });
    })
    .then(() => {
      console.log(`done`);
      console.log(`View the pull request at ${pullRequestUrl}`);
      return pullRequestUrl;
    })
    .catch(error => {
      console.error(`Error: ${error.message}`);
      return Promise.reject(error);
    });

  /**
   * @returns {!string} A Markdown list of all fixture warnings and errors.
   */
  function getFixtureWarningsErrorsMarkdownList() {
    let markdownList = ``;

    if (!(`errors` in out)) {
      out.errors = {};
    }
    if (!(`warnings` in out)) {
      out.warnings = {};
    }

    for (const fixtureKey of Object.keys(out.fixtures)) {
      const fixtureErrors = out.errors[fixtureKey] || [];
      const fixtureWarnings = out.warnings[fixtureKey] || [];

      const messages = fixtureErrors.map(
        error => `  - :x: ${error}`
      ).concat(fixtureWarnings.map(
        warning => `  - :warning: ${warning}`
      ));

      if (messages.length) {
        markdownList += `* ${fixtureKey}\n${messages.join(`\n`)}\n`;
      }
    }

    return markdownList;
  }

  /**
   * @returns {!string} The submitter of the fixture(s), or the first author of the first fixture.
   */
  function getSubmitterNameMarkdown() {
    if (out.submitter) {
      return /^[a-zA-Z0-9]+$/.test(out.submitter) ? `@${out.submitter}` : `**${out.submitter}**`;
    }

    const firstFixtureKey = Object.keys(out.fixtures)[0];
    const firstFixtureAuthor = out.fixtures[firstFixtureKey].meta.authors[0];

    return `**${firstFixtureAuthor}**`;
  }

  /**
   * @callback newContentFunction
   * @param {?string} oldFileContent The text content of the old file, or null if it has not existed yet.
   * @return {!string} The new file text content.
   */

  /**
   * @param {!string} filename The path of the file (relative to the repository root) to add or update.
   * @param {!string} displayName A shorter file name to display in commit messages and PR body.
   * @param {!newContentFunction} newContentFunction The callback to get the new file content from.
   * @returns {!Promise} Promise that is resolved when the file is successfully / erroneously updated / added.
   */
  function addOrUpdateFile(filename, displayName, newContentFunction) {
    console.log(`does ${displayName} exist?`);

    return githubClient.repos.getContent({
      owner: `OpenLightingProject`,
      repo: repository,
      path: filename
    })
      .then(result => {
        console.log(`yes -> update it ...`);

        const oldFileContent = Buffer.from(result.data.content, result.data.encoding).toString(`utf8`);
        const newFileContent = newContentFunction(oldFileContent);

        if (oldFileContent === newFileContent) {
          console.log(`no need to update, files are the same`);
          return Promise.resolve();
        }

        return githubClient.repos.updateFile({
          owner: `OpenLightingProject`,
          repo: repository,
          path: filename,
          message: `Update ${displayName} via editor`,
          content: encodeBase64(newFileContent),
          sha: result.data.sha,
          branch: branchName
        })
          .then(() => {
            console.log(`done`);
            changedFiles.push(`Update ${displayName}`);
          })
          .catch(error => {
            console.error(`Error updating ${displayName}: ${error.message}`);
            warnings.push(`* Error updating ${displayName}: \`${error.message}\``);
          });
      })
      .catch(error => {
        console.log(`no -> create it ...`);
        return githubClient.repos.createFile({
          owner: `OpenLightingProject`,
          repo: repository,
          path: filename,
          message: `Add ${displayName} via editor`,
          content: encodeBase64(newContentFunction(null)),
          branch: branchName
        })
          .then(() => {
            console.log(`done`);
            changedFiles.push(`Add ${displayName}`);
          })
          .catch(error => {
            console.error(`Error adding ${displayName}: ${error.message}`);
            warnings.push(`* Error adding ${displayName}: \`${error.message}\``);
          });
      });
  }

  /**
   * @param {!object} register The register to sort.
   * @returns {!object} The sorted register.
   */
  function getSortedRegister(register) {
    const newRegister = {
      filesystem: {},
      manufacturers: {},
      categories: {},
      contributors: {},
      rdm: {}
    };

    // copy sorted filesystem into register
    for (const fixKey of Object.keys(register.filesystem).sort(localeSort)) {
      newRegister.filesystem[fixKey] = register.filesystem[fixKey];
    }

    // copy sorted manufacturers into register
    for (const man of Object.keys(register.manufacturers).sort(localeSort)) {
      newRegister.manufacturers[man] = register.manufacturers[man].sort(localeSort);
    }

    // copy sorted categories into register
    for (const cat of Object.keys(register.categories).sort(localeSort)) {
      newRegister.categories[cat] = register.categories[cat].sort(localeSort);
    }

    // copy sorted contributors into register
    const sortedContributors = Object.keys(register.contributors).sort((a, b) => {
      const fixturesDelta = register.contributors[b].length - register.contributors[a].length;
      const nameDelta = a > b ? 1 : a < b ? -1 : 0;
      return fixturesDelta !== 0 ? fixturesDelta : nameDelta;
    });
    for (const contributor of sortedContributors) {
      newRegister.contributors[contributor] = register.contributors[contributor].sort(localeSort);
    }

    // the higher the value, the higher the rank if the dates are the same
    const lastActionRankMapping = {
      'modified': 10,
      'imported': 20,
      'created': 30
    };

    // add fixture list sorted by lastModifyDate
    newRegister.lastUpdated = Object.keys(newRegister.filesystem).filter(
      fixtureKey => `lastModifyDate` in register.filesystem[fixtureKey]
    ).sort((a, b) => {
      const fixA = register.filesystem[a];
      const fixB = register.filesystem[b];
      const dateDelta = new Date(fixB.lastModifyDate) - new Date(fixA.lastModifyDate);
      const actionDelta = lastActionRankMapping[fixB.lastAction] - lastActionRankMapping[fixA.lastAction];
      const keyDelta = a > b ? 1 : a < b ? -1 : 0;
      return dateDelta !== 0 ? dateDelta : (actionDelta !== 0 ? actionDelta : keyDelta);
    });

    // copy sorted RDM data into register
    for (const manId of Object.keys(register.rdm).sort(localeSort)) {
      newRegister.rdm[manId] = {
        key: register.rdm[manId].key,
        models: {}
      };

      for (const fixId of Object.keys(register.rdm[manId].models).sort(localeSort)) {
        newRegister.rdm[manId].models[fixId] = register.rdm[manId].models[fixId];
      }
    }

    return newRegister;
  }
};

/**
 * @param {!string} string The string to encode in base 64.
 * @returns {!string} The encoded string.
 */
function encodeBase64(string) {
  return Buffer.from(string).toString(`base64`);
}

/**
 * JSON.stringify with an extra newline character at the end.
 * @param {!object} obj JSON object.
 * @returns {!string} String representing the given object.
 */
function prettyJsonStringify(obj) {
  const str = JSON.stringify(obj, null, 2);

  return `${str}\n`;
}

/**
 * Function to pass into Array.sort().
 * @param {!string} a The first string.
 * @param {!string} b The second string.
 * @returns {!number} A number indicating the order of the two strings.
 */
function localeSort(a, b) {
  return a.localeCompare(b, `en`, {
    numeric: true
  });
}
<|endoftext|>


<commit_before>module.exports = function () {

  var path           = require('path'),
      Promise        = require('bluebird'),
      SessionManager = require('../session-manager.js'),
      chaiAsPromised = require('chai-as-promised'),
      log            = require('../log'),
      fs             = require('fs-extra');

  var passed = true;

  this.registerHandler('BeforeFeatures', function (event, callback) {

    log.debug('[chimp][hooks] Starting BeforeFeatures');

    var setupBrowser = Promise.promisify(function (callback) {

      log.debug('[chimp][world] getting browser');
      var configPath = path.resolve(process.cwd(), process.env['chimp.path'], 'chimp.js');

      var _translateLogLevel = function () {
        if (process.env['chimp.log'] === 'info' ||
          process.env['chimp.log'] === 'warn' ||
          process.env['chimp.log'] === 'error') {
          return 'silent'
        }
        return process.env['chimp.log'];
      };

      var browser;

      global.sessionManager = new SessionManager({
        port: process.env['chimp.port'],
        browser: process.env['chimp.browser'],
        deviceName: process.env['chimp.deviceName']
      });

      var setBrowser = function (err, res) {
        global.browser = res;
        chaiAsPromised.transferPromiseness = global.browser.transferPromiseness;
        callback(err, res)
      };

      if (fs.existsSync(configPath)) {
        // load monkey webdriver configuration
        browser = require(configPath)(global.sessionManager, setBrowser);

      } else {
        log.debug('[chimp][world] custom chimp.js not found, loading defaults');
        var webdriverOptions = {
          waitforTimeout: parseInt(process.env['chimp.waitForTimeout']),
          desiredCapabilities: {
            browserName: process.env['chimp.browser'],
            platform: process.env['chimp.platform'],
            name: process.env['chimp.name']
          },
          user: process.env['chimp.user'] || process.env.SAUCE_USERNAME,
          key: process.env['chimp.key'] || process.env.SAUCE_ACCESS_KEY,
          host: process.env['chimp.host'],
          port: process.env['chimp.port'],
          logLevel: _translateLogLevel(),
          screenshotPath: process.env['chimp.screenshotPath']
        };
        log.debug('[chimp][world] webdriverOptions are ', JSON.stringify(webdriverOptions));

        global.sessionManager.remote(webdriverOptions, setBrowser);
      }

    });

    var initBrowser = Promise.promisify(function (callback) {
      log.debug('[chimp][hooks] init browser');
      global.browser.init(function () {

        log.debug('[chimp][hooks] init browser callback');

        global.browser.screenshotsCount = 0;
        global.browser.addCommand('capture', function (name, captureCallback) {
          var location = (global.browser.screenshotsCount++) + '-' + name + '_' + new Date().getTime() + '.png';
          fs.mkdirsSync(process.env['chimp.screenshotsPath']);
          var ssPath = path.join(process.env['chimp.screenshotsPath'], location);
          log.debug('[chimp][hooks] saving screenshot to', ssPath);
          return this.saveScreenshot(ssPath, false,
            function () {
              log.debug('[chimp][hooks] saved screenshot to', ssPath);
              captureCallback();
            });
        });

        global.browser.timeoutsAsyncScript(parseInt(process.env['chimp.timeoutsAsyncScript']), function () {
          log.debug('[chimp][hooks] set timeoutsAsyncScript');
          callback();
        });

        if (process.env['chimp.browser'] === 'phantomjs') {
          global.browser.setViewportSize({
            width: process.env['chimp.phantom_w'] || 1280,
            height: process.env['chimp.phantom_h'] || 1024
          });
        }
      });

    });

    var setupDdp = function (callback) {
      log.debug('[chimp][hooks] setup DDP');
      if (process.env['chimp.ddp']) {
        log.debug('[chimp][hooks] connecting via DDP to', process.env['chimp.ddp']);
        return global.ddp.connect(function (error) {
          if (error) {
            log.error('[chimp][hooks] connecting via DDP error', error);
            callback(error);
          } else {
            log.error('[chimp][hooks] connecting via DDP had no error');
            callback();
          }
        });
      } else {
        log.debug('[chimp][hooks] DDP not required');
        // worthless http://en.wiktionary.org/wiki/empty_promise
        Promise.promisify(function () {});
      }
    };

    setupBrowser().then(function () {
      return Promise.all([
        initBrowser(),
        setupDdp()
      ])
    }).then(
      function () {
        log.debug('[chimp][hooks] Finished BeforeFeatures');
        if (this.UserDefinedBeforeFeatures) {
          log.debug('[chimp][hooks] User-defined BeforeFeatures found, calling');
          global.UserDefinedBeforeFeatures.apply(this, [function () {
            callback();
          }]);
        } else {
          log.debug('[chimp][hooks] User-defined BeforeFeatures not found, finishing up');
          callback();
        }
      },
      function (error) {
        log.error('[chimp][hooks] BeforeFeatures hook had error');
        log.error(error);
        process.exit(2);
      }
    )

  });

  /**
   * Store browser test status so we can notify SauceLabs
   *
   * @param {Function} event
   * @param {Function} callback
   */
  this.StepResult(function (event, callback) {
    var stepResult = event.getPayloadItem('stepResult');
    if (process.env['chimp.screenshotsOnError'] !== 'false' && !stepResult.isSuccessful()) {
      log.debug('[chimp][hooks] step failed - capturing screenshot');
      global.browser.capture('step_failed', callback);
    } else {
      callback();
    }

  });

  /**
   * After features have run we close the browser and optionally notify
   * SauceLabs
   *
   * @param {Function} event
   * @param {Function} callback
   */
  this.registerHandler('AfterFeatures', function (event, callback) {
    log.debug('[chimp][hooks] Starting AfterFeatures');

    var done = function () {
      log.debug('[chimp][hooks] Finished AfterFeatures');
      callback();
    };
    if (process.env['chimp.browser'] !== 'phantomjs') {
      log.debug('[chimp][hooks] Ending browser session');

      global.sessionManager.killCurrentSession(function () {
        log.debug('[chimp][hooks] Ended browser sessions');
        done();
      });

    } else {
      done();
    }

  });

  process.on('unhandledRejection', function (reason /*, promise*/) {
    log.error('[chimp] Detected an unhandledRejection. Reason:');
    log.error(reason);
    log.error('[chimp][hooks] Forcibly exiting Cucumber');
    process.send(JSON.stringify(reason));
    process.exit(2);
  });

  process.on('SIGINT', function () {
    log.debug('[chimp][hooks] Received SIGINT process event, ending browser session');
    global.browser.
      end().
      then(function () {
        log.debug('[chimp][hooks] ended browser session');
        process.exit();
      });
  });

};
<commit_msg>added some intelligence to the errors reported from unhandledRejections<commit_after>module.exports = function () {

  var path           = require('path'),
      Promise        = require('bluebird'),
      SessionManager = require('../session-manager.js'),
      chaiAsPromised = require('chai-as-promised'),
      colors         = require('colors'),
      log            = require('../log'),
      fs             = require('fs-extra');

  this.registerHandler('BeforeFeatures', function (event, callback) {

    log.debug('[chimp][hooks] Starting BeforeFeatures');

    var setupBrowser = Promise.promisify(function (callback) {

      log.debug('[chimp][world] getting browser');
      var configPath = path.resolve(process.cwd(), process.env['chimp.path'], 'chimp.js');

      var _translateLogLevel = function () {
        if (process.env['chimp.log'] === 'info' ||
          process.env['chimp.log'] === 'warn' ||
          process.env['chimp.log'] === 'error') {
          return 'silent'
        }
        return process.env['chimp.log'];
      };

      var browser;

      global.sessionManager = new SessionManager({
        port: process.env['chimp.port'],
        browser: process.env['chimp.browser'],
        deviceName: process.env['chimp.deviceName']
      });

      var setBrowser = function (err, res) {
        global.browser = res;
        chaiAsPromised.transferPromiseness = global.browser.transferPromiseness;
        callback(err, res)
      };

      if (fs.existsSync(configPath)) {
        browser = require(configPath)(global.sessionManager, setBrowser);
      } else {
        log.debug('[chimp][world] custom chimp.js not found, loading defaults');
        var webdriverOptions = {
          waitforTimeout: parseInt(process.env['chimp.waitForTimeout']),
          desiredCapabilities: {
            browserName: process.env['chimp.browser'],
            platform: process.env['chimp.platform'],
            name: process.env['chimp.name']
          },
          user: process.env['chimp.user'] || process.env.SAUCE_USERNAME,
          key: process.env['chimp.key'] || process.env.SAUCE_ACCESS_KEY,
          host: process.env['chimp.host'],
          port: process.env['chimp.port'],
          logLevel: _translateLogLevel(),
          screenshotPath: process.env['chimp.screenshotPath']
        };
        log.debug('[chimp][world] webdriverOptions are ', JSON.stringify(webdriverOptions));

        global.sessionManager.remote(webdriverOptions, setBrowser);
      }

    });

    var initBrowser = Promise.promisify(function (callback) {
      log.debug('[chimp][hooks] init browser');
      global.browser.init(function () {

        log.debug('[chimp][hooks] init browser callback');

        global.browser.screenshotsCount = 0;
        global.browser.addCommand('capture', function (name, captureCallback) {
          var location = (global.browser.screenshotsCount++) + '-' + name + '_' + new Date().getTime() + '.png';
          fs.mkdirsSync(process.env['chimp.screenshotsPath']);
          var ssPath = path.join(process.env['chimp.screenshotsPath'], location);
          log.debug('[chimp][hooks] saving screenshot to', ssPath);
          return this.saveScreenshot(ssPath, false,
            function () {
              log.debug('[chimp][hooks] saved screenshot to', ssPath);
              captureCallback();
            });
        });

        global.browser.timeoutsAsyncScript(parseInt(process.env['chimp.timeoutsAsyncScript']), function () {
          log.debug('[chimp][hooks] set timeoutsAsyncScript');
          callback();
        });

        if (process.env['chimp.browser'] === 'phantomjs') {
          global.browser.setViewportSize({
            width: process.env['chimp.phantom_w'] || 1280,
            height: process.env['chimp.phantom_h'] || 1024
          });
        }
      });

    });

    var setupDdp = function (callback) {
      log.debug('[chimp][hooks] setup DDP');
      if (process.env['chimp.ddp']) {
        log.debug('[chimp][hooks] connecting via DDP to', process.env['chimp.ddp']);
        return global.ddp.connect(function (error) {
          if (error) {
            log.error('[chimp][hooks] connecting via DDP error', error);
            callback(error);
          } else {
            log.error('[chimp][hooks] connecting via DDP had no error');
            callback();
          }
        });
      } else {
        log.debug('[chimp][hooks] DDP not required');
        // worthless http://en.wiktionary.org/wiki/empty_promise
        Promise.promisify(function () {});
      }
    };

    setupBrowser().then(function () {
      return Promise.all([
        initBrowser(),
        setupDdp()
      ])
    }).then(
      function () {
        log.debug('[chimp][hooks] Finished BeforeFeatures');
        if (this.UserDefinedBeforeFeatures) {
          log.debug('[chimp][hooks] User-defined BeforeFeatures found, calling');
          global.UserDefinedBeforeFeatures.apply(this, [function () {
            callback();
          }]);
        } else {
          log.debug('[chimp][hooks] User-defined BeforeFeatures not found, finishing up');
          callback();
        }
      },
      function (error) {
        log.error('[chimp][hooks] BeforeFeatures hook had error');
        log.error(error);
        process.exit(2);
      }
    )

  });

  /**
   * Store browser test status so we can notify SauceLabs
   *
   * @param {Function} event
   * @param {Function} callback
   */
  var lastStep;
  this.StepResult(function (event, callback) {
    var stepResult = event.getPayloadItem('stepResult');
    lastStep = stepResult.getStep();
    if (process.env['chimp.screenshotsOnError'] !== 'false' && !stepResult.isSuccessful()) {
      log.debug('[chimp][hooks] step failed - capturing screenshot');
      global.browser.capture('step_failed', callback);
    } else {
      callback();
    }

  });

  /**
   * After features have run we close the browser and optionally notify
   * SauceLabs
   *
   * @param {Function} event
   * @param {Function} callback
   */
  this.registerHandler('AfterFeatures', function (event, callback) {
    log.debug('[chimp][hooks] Starting AfterFeatures');

    var done = function () {
      log.debug('[chimp][hooks] Finished AfterFeatures');
      callback();
    };
    if (process.env['chimp.browser'] !== 'phantomjs') {
      log.debug('[chimp][hooks] Ending browser session');

      global.sessionManager.killCurrentSession(function () {
        log.debug('[chimp][hooks] Ended browser sessions');
        done();
      });

    } else {
      done();
    }

  });

  process.on('unhandledRejection', function (reason, promise) {
    log.error('[chimp] Detected an unhandledRejection.'.red);

    var hint;
    try {
      if (reason.type === 'CommandError' && reason.message === 'Promise never resolved with an truthy value') {
        reason.type += 'WebdriverIO CommandError (Promise never resolved with an truthy value)';
        reason.message = 'This usually happens when WebdriverIO assertions fail or timeout.';
        hint = 'HINT: Check the step AFTER [' + lastStep.getKeyword() + lastStep.getName() + ']';
        var uri = lastStep.getUri();
        uri = uri.substring(process.env.PWD.length, uri.length);
        hint += ' (' + uri + ': >' + lastStep.getLine() + ')';
      }
    } catch (e) {
      log.debug('[chimp][hooks] Could not provide error hint');
    }

    log.error('[chimp][hooks] Reason:'.red);
    log.error('[chimp][hooks]'.red, reason.type.red);
    log.error('[chimp][hooks]'.red, reason.message.red);
    log.error(hint.yellow);

    log.debug('[chimp][hooks] Promise:', JSON.parse(JSON.stringify(promise)));
    log.debug('[chimp][hooks] Forcibly exiting Cucumber');

    reason.message += '\n' + hint;

    process.send(JSON.stringify(reason));
    process.exit(2);
  });

  process.on('SIGINT', function () {
    log.debug('[chimp][hooks] Received SIGINT process event, ending browser session');
    global.browser.
      end().
      then(function () {
        log.debug('[chimp][hooks] ended browser session');
        process.exit();
      });
  });

};
<|endoftext|>


<commit_before>const EventEmitter = require('events')
const Messages = require('./messages.js')
const MessageFactory = require('./message-factory.js')
const debug = require('debug')('slsk:peer:i')

module.exports = class DistributedPeer extends EventEmitter {
  constructor (socket, peer) {
    super()
    this.conn = socket
    this.peer = peer

    this.conn.on('connect', () => {
      if (peer.token) {
        this.conn.write(MessageFactory
          .to.peer
          .peerInit(peer.user, peer.type, peer.token)
          .getBuff())
        let buf = Buffer.from('05000000' + '00' + peer.token, 'hex')
        this.conn.write(buf)
      }
    })

    this.conn.on('error', error => {
      debug(`${peer.user} error ${error.code}`)
      this.emit('disconnect', {})
    })

    this.conn.on('end', () => {
      debug(`${peer.user} connection ended`)
      this.emit('disconnect', {})
    })

    let msgs = new Messages()

    this.conn.on('data', data => {
      msgs.write(data)
    })

    msgs.on('message', msg => {
      let size = msg.int32()
      if (size <= 4) return

      let code = msg.int8()
      switch (code) {
        case 3: {
          let unknown = msg.int32()
          let user = msg.str()
          let ticket = msg.readRawHexStr(4)
          let query = msg.str()
          debug(`${peer.user} Search Request from ${user}, ticket ${ticket}. query: ${query}. unknown field: ${unknown}`)
          break
        }
        case 4: {
          let branchLevel = msg.int32()
          debug(`${peer.user} Branch Level ${branchLevel}`)
          break
        }
        case 5: {
          let branchRoot = msg.str()
          debug(`${peer.user} Branch Root ${branchRoot}`)
          break
        }
        default: {
          debug(`${peer.user} unknown distributed message code ${code}`)
        }
      }
    })
  }

  transferRequest (file, token) {
    debug(`Transfer request ${file}`)
    this.conn.write(MessageFactory
      .to.peer
      .transferRequest(file, token)
      .getBuff())
  }

  setAddress (host, port) {
    debug(`setAddress for ${this.peer.user}: ${host} ${port}`)
    this.peer.host = host
    this.peer.port = port
  }

  destroy () {
    this.conn.destroy()
  }
}
<commit_msg>Change debug<commit_after>const EventEmitter = require('events')
const Messages = require('./messages.js')
const MessageFactory = require('./message-factory.js')
const debug = require('debug')('slsk:distributed-peer:i')

module.exports = class DistributedPeer extends EventEmitter {
  constructor (socket, peer) {
    super()
    this.conn = socket
    this.peer = peer

    this.conn.on('connect', () => {
      if (peer.token) {
        this.conn.write(MessageFactory
          .to.peer
          .peerInit(peer.user, peer.type, peer.token)
          .getBuff())
        let buf = Buffer.from('05000000' + '00' + peer.token, 'hex')
        this.conn.write(buf)
      }
    })

    this.conn.on('error', error => {
      debug(`${peer.user} error ${error.code}`)
      this.emit('disconnect', {})
    })

    this.conn.on('end', () => {
      debug(`${peer.user} connection ended`)
      this.emit('disconnect', {})
    })

    let msgs = new Messages()

    this.conn.on('data', data => {
      msgs.write(data)
    })

    msgs.on('message', msg => {
      let size = msg.int32()
      if (size <= 4) return

      let code = msg.int8()
      switch (code) {
        case 3: {
          let unknown = msg.int32()
          let user = msg.str()
          let ticket = msg.readRawHexStr(4)
          let query = msg.str()
          debug(`${peer.user} Search Request from ${user}, ticket ${ticket}. query: ${query}. unknown field: ${unknown}`)
          break
        }
        case 4: {
          let branchLevel = msg.int32()
          debug(`${peer.user} Branch Level ${branchLevel}`)
          break
        }
        case 5: {
          let branchRoot = msg.str()
          debug(`${peer.user} Branch Root ${branchRoot}`)
          break
        }
        default: {
          debug(`${peer.user} unknown distributed message code ${code}`)
        }
      }
    })
  }

  transferRequest (file, token) {
    debug(`Transfer request ${file}`)
    this.conn.write(MessageFactory
      .to.peer
      .transferRequest(file, token)
      .getBuff())
  }

  setAddress (host, port) {
    debug(`setAddress for ${this.peer.user}: ${host} ${port}`)
    this.peer.host = host
    this.peer.port = port
  }

  destroy () {
    this.conn.destroy()
  }
}
<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('underscore');
var cdb = require('cartodb.js');
var template = require('./template.tpl');

/**
 * Standard widget dropdown view
 *
 */
module.exports = cdb.core.View.extend({

  _WIDGET_BOTTOM_PADDING: 20,

  className: 'CDB-Dropdown',

  events: {
    'click .js-pin': '_pin',
    'click .js-toggle': '_toggle'
  },

  initialize: function (opts) {
    if (!opts.target) {
      throw new Error('target is not defined');
    }

    this.viewModel = new cdb.core.Model({ open: false });

    this._$target = this.options.target;
    this._$container = this.options.container;
    this._initBinds();
  },

  render: function () {
    this.$el.html(
      template()
    );

    return this;
  },

  _initBinds: function () {
    this.add_related_model(this.viewModel);

    this.viewModel.bind('change:open', this._onChangeOpen, this);

    this._$target.click(
      _.bind(this._toggleClick, this)
    );
  },

  _bindGlobalClick: function () {
    $(document).bind('click.' + this.cid, _.bind(this._onGlobalClick, this));
  },

  _unbindGlobalClick: function () {
    $(document).unbind('click.' + this.cid);
  },

  _bindESC: function () {
    $(document).bind('keyup.' + this.cid, _.bind(this._onKeyUp, this));
  },

  _unbindESC: function () {
    $(document).unbind('keyup.' + this.cid);
  },

  _onGlobalClick: function (ev) {
    if (this._$target.get(0) !== $(ev.target).closest(this._$target).get(0)) {
      this.viewModel.set('open', false);
    }
  },

  _onKeyUp: function (ev) {
    if (ev.keyCode === 27) {
      this.viewModel.set('open', false);
      return false;
    }
  },

  _onChangeOpen: function () {
    if (this.viewModel.get('open')) {
      this._open();
    } else {
      this._close();
    }
  },

  _pin: function () {
    this.viewModel.set('open', false);
    this.trigger('click', 'pin');
  },

  _toggle: function () {
    this.viewModel.set('open', false);
    this.trigger('click', 'toggle');
  },

  _open: function () {
    this._bindESC();
    this._bindGlobalClick();

    this.render();
    this._$container.append(this.$el);
    this.$el.show();

    this._adjustVerticalPosition();
  },

  _adjustVerticalPosition: function() {
    var bodyHeight = $('body').height();
    var bottom = this.$el.offset().top + this.$el.height();

    if (bottom + this._WIDGET_BOTTOM_PADDING> bodyHeight) {
      this.$el.addClass('has-top-position');
    }
  },

  _close: function () {
    this._unbindESC();
    this._unbindGlobalClick();
    this.$el.hide();
  },

  _toggleClick: function () {
    this.viewModel.set('open', !this.viewModel.get('open'));
  },

  clean: function () {
    this._unbindESC();
    this._unbindGlobalClick();
    this._$target.off('click');
    cdb.core.View.prototype.clean.call(this);
  }
});
<commit_msg>Fixes warnings<commit_after>var $ = require('jquery');
var _ = require('underscore');
var cdb = require('cartodb.js');
var template = require('./template.tpl');

/**
 * Standard widget dropdown view
 *
 */
module.exports = cdb.core.View.extend({

  _WIDGET_BOTTOM_PADDING: 20,

  className: 'CDB-Dropdown',

  events: {
    'click .js-pin': '_pin',
    'click .js-toggle': '_toggle'
  },

  initialize: function (opts) {
    if (!opts.target) {
      throw new Error('target is not defined');
    }

    this.viewModel = new cdb.core.Model({ open: false });

    this._$target = this.options.target;
    this._$container = this.options.container;
    this._initBinds();
  },

  render: function () {
    this.$el.html(
      template()
    );

    return this;
  },

  _initBinds: function () {
    this.add_related_model(this.viewModel);

    this.viewModel.bind('change:open', this._onChangeOpen, this);

    this._$target.click(
      _.bind(this._toggleClick, this)
    );
  },

  _bindGlobalClick: function () {
    $(document).bind('click.' + this.cid, _.bind(this._onGlobalClick, this));
  },

  _unbindGlobalClick: function () {
    $(document).unbind('click.' + this.cid);
  },

  _bindESC: function () {
    $(document).bind('keyup.' + this.cid, _.bind(this._onKeyUp, this));
  },

  _unbindESC: function () {
    $(document).unbind('keyup.' + this.cid);
  },

  _onGlobalClick: function (ev) {
    if (this._$target.get(0) !== $(ev.target).closest(this._$target).get(0)) {
      this.viewModel.set('open', false);
    }
  },

  _onKeyUp: function (ev) {
    if (ev.keyCode === 27) {
      this.viewModel.set('open', false);
      return false;
    }
  },

  _onChangeOpen: function () {
    if (this.viewModel.get('open')) {
      this._open();
    } else {
      this._close();
    }
  },

  _pin: function () {
    this.viewModel.set('open', false);
    this.trigger('click', 'pin');
  },

  _toggle: function () {
    this.viewModel.set('open', false);
    this.trigger('click', 'toggle');
  },

  _open: function () {
    this._bindESC();
    this._bindGlobalClick();

    this.render();
    this._$container.append(this.$el);
    this.$el.show();

    this._adjustVerticalPosition();
  },

  _adjustVerticalPosition: function () {
    var bodyHeight = $('body').height();
    var bottom = this.$el.offset().top + this.$el.height();

    if (bottom + this._WIDGET_BOTTOM_PADDING > bodyHeight) {
      this.$el.addClass('has-top-position');
    }
  },

  _close: function () {
    this._unbindESC();
    this._unbindGlobalClick();
    this.$el.hide();
  },

  _toggleClick: function () {
    this.viewModel.set('open', !this.viewModel.get('open'));
  },

  clean: function () {
    this._unbindESC();
    this._unbindGlobalClick();
    this._$target.off('click');
    cdb.core.View.prototype.clean.call(this);
  }
});
<|endoftext|>