<commit_before>/**
 *

 *  Constructs a ProAct.Stream. The stream is a simple {@link ProAct.Actor}, without state.
 *

 *

 *  The streams are ment to emit values, events, changes and can be plugged into another actor.
 *  For example you can connect many streams, to merge them and to divide them, to plug them into properties.
 *

 *

 *  The reactive environment consists of the properties and the objects containing them, but
 *  the outside world is not reactive. It is possible to use the ProAct.Streams as connections from the
 *  outside world to the reactive environment.
 *

 *

 *    The transformations can be used to change the events or values emitetted.
 *

 *

 *  ProAct.Stream is part of the streams module of ProAct.js.
 *

 *
 * @class ProAct.Stream
 * @extends ProAct.Actor
 * @param {String} queueName
 *      The name of the queue all the updates should be pushed to.
 *

 *        If this parameter is null/undefined the default queue of
 *        {@link ProAct.flow} is used.
 *

 *

 *        If this parameter is not a string it is used as the
 *        source.
 *

 * @param {ProAct.Actor} source
 *      A default source of the stream, can be null.
 * @param {Array} transforms
 *      A list of transformation to be used on all incoming chages.
 */
function Stream (queueName, source, transforms) {
  if (queueName && !P.U.isString(queueName)) {
    transforms = source;
    source = queueName;
    queueName = null;
  }
  P.Actor.call(this, queueName, transforms);

  this.sourceNumber = 0;

  if (source) {
    this.into(source);
  }
}
ProAct.Stream = ProAct.S = Stream;

ProAct.Stream.prototype = P.U.ex(Object.create(P.Actor.prototype), {

  /**
   * Reference to the constructor of this object.
   *
   * @memberof ProAct.Stream
   * @instance
   * @constant
   * @type {Object}
   * @default ProAct.Stream
   */
  constructor: ProAct.Stream,

  /**
   * Creates the event to be send to the listeners on update.
   *

   *  Streams don't create new events by default, the event is the source.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeEvent
   * @param {ProAct.Event} source
   *      The source event of the event. It can be null
   * @return {ProAct.Event}
   *      The event.
   */
  makeEvent: function (source) {
    return source;
  },

  /**
   * Creates the listener of this stream.
   *

   *  The listener of the stream just calls the method {@link ProAct.Stream#trigger} with the incoming event/value.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeListener
   * @return {Object}
   *      The listener of this stream.
   */
  makeListener: function () {
    if (!this.listener) {
      var stream = this;
      this.listener = function (event) {
        stream.trigger(event, true);
      };
    }

    return this.listener;
  },

  /**
   * Creates the error listener of this stream.
   *

   *  The listener just calls {@link ProAct.Stream#triggerErr} of this with the incoming error.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeErrListener
   * @return {Object}
   *      The error listener of this stream.
   */
  makeErrListener: function () {
    if (!this.errListener) {
      var stream = this;
      this.errListener = function (error) {
        stream.triggerErr(error);
      };
    }

    return this.errListener;
  },

  /**
   * Creates the closing listener of this stream.
   *

   *  The listener just calls {@link ProAct.Stream#triggerClose} of this with the incoming closing data.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeCloseListener
   * @return {Object}
   *      The closing listener of this stream.
   */
  makeCloseListener: function () {
    if (!this.closeListener) {
      var stream = this;
      this.closeListener = function (error) {
        stream.triggerClose(error);
      };
    }

    return this.closeListener;
  },

  /**
   * Defers a ProAct.Actor listener.
   *

   *  For streams this means pushing it to active flow using {@link ProAct.Flow#push}.
   *  If the listener is object with 'property' field, it is done using {@link ProAct.Actor#defer}.
   *  That way the reactive environment is updated only once, but the streams are not part of it.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method defer
   * @param {Object} event
   *      The event/value to pass to the listener.
   * @param {Object} listener
   *      The listener to defer. It should be a function or object defining the call method.
   * @return {ProAct.Actor}
   *      this
   * @see {@link ProAct.Actor#willUpdate}
   * @see {@link ProAct.Actor#makeListener}
   * @see {@link ProAct.flow}
   */
  defer: function (event, listener) {
    if (!listener) {
      return;
    }

    if (listener.property) {
      P.Actor.prototype.defer.call(this, event, listener);
      return;
    }
    var queueName = (listener.queueName) ? listener.queueName : this.queueName;

    if (P.U.isFunction(listener)) {
      P.flow.push(queueName, listener, [event]);
    } else {
      P.flow.push(queueName, listener, listener.call, [event]);
    }
  },

  /**
   *

   *  Triggers a new event/value to the stream. Anything that is listening for events from
   *  this stream will get updated.
   *

   *

   *  ProAct.Stream.t is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method trigger
   * @param {Object} event
   *      The event/value to pass to trigger.
   * @param {Boolean} useTransformations
   *      If the stream should transform the triggered value. By default it is true (if not passed)
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  trigger: function (event, useTransformations) {
    if (useTransformations === undefined) {
      useTransformations = true;
    }

    return this.go(event, useTransformations);
  },

  /**
   *

   *  Triggers all the passed params, using transformations.
   *

   *

   *  ProAct.Stream.tt is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerMany
   * @param [...]
   *      A list of events/values to trigger
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Stream#trigger}
   */
  triggerMany: function () {
    var i, args = slice.call(arguments), ln = args.length;

    for (i = 0; i < ln; i++) {
      this.trigger(args[i], true);
    }

    return this;
  },

  /**
   *

   *  Triggers a new error to the stream. Anything that is listening for errors from
   *  this stream will get updated.
   *

   *

   *  ProAct.Stream.te is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerErr
   * @param {Error} err
   *      The error to trigger.
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  triggerErr: function (err) {
    return this.update(err, 'error');
  },

  /**
   *

   *  Triggers a closing event to the stream. Anything that is listening for closing events from
   *  this stream will get updated.
   *

   *

   *  The stream will be closed and unusable.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerClose
   * @param {Object} data
   *      Data connected to the closing event.
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  triggerClose: function (data) {
    return this.update(data, 'close');
  },

  // private
  go: function (event, useTransformations) {
    if (useTransformations) {
      try {
        event = P.Actor.transform(this, event);
      } catch (e) {
        this.triggerErr(e);
        return this;
      }
    }

    if (event === P.Actor.BadValue) {
      return this;
    }

    return this.update(event);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and mapping
   * the passed mapping function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method map
   * @param {Object} mappingFunction
   *      Function or object with a call method to use as map function.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the mapping applied.
   * @see {@link ProAct.Actor#mapping}
   */
  map: function (mappingFunction) {
    return new P.S(this).mapping(mappingFunction);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and filtering
   * the passed filtering function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method filter
   * @param {Object} filteringFunction
   *      The filtering function or object with a call method, should return boolean.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the filtering applied.
   * @see {@link ProAct.Actor#filtering}
   */
  filter: function (filteringFunction) {
    return new P.S(this).filtering(filteringFunction);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and accumulation
   * the passed accumulation function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method accumulate
   * @param {Object} initVal
   *      Initial value for the accumulation. For example '0' for sum.
   * @param {Object} accumulationFunction
   *      The function to accumulate.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the accumulation applied.
   * @see {@link ProAct.Actor#accumulation}
   */
  accumulate: function (initVal, accumulationFunction) {
    return new P.S(this).accumulation(initVal, accumulationFunction);
  },

  /**
   * Creates a new ProAct.Stream instance that merges this with other streams.
   * The new instance will have new value on value from any of the source streams.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method merge
   * @param [...]
   *      A list of streams to be set as sources.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the sources this and all the passed streams.
   */
  merge: function () {
    var sources = [this].concat(slice.call(arguments)),
        result = new P.S();

    return P.S.prototype.into.apply(result, sources);
  },

  into: function () {
    ProAct.Actor.prototype.into.apply(this, arguments);

    this.sourceNumber += arguments.length;

    return this;
  },

  canDestroy: function () {
    this.sourceNumber -= 1;

    return this.sourceNumber <= 0;
  }
});

P.U.ex(P.F.prototype, {

  /**
   * Retrieves the errStream for logging errors from this flow.
   * If there is no error stream, it is created.
   *
   * @memberof ProAct.Flow
   * @instance
   * @method errStream
   * @return {ProAct.Stream}
   *      The error stream of the flow.
   */
  errStream: function () {
    if (!this.errStreamVar) {
      this.errStreamVar = new P.S();
    }

    return this.errStreamVar;
  }
});

P.S.prototype.t = P.S.prototype.trigger;
P.S.prototype.tt = P.S.prototype.triggerMany;
P.S.prototype.te = P.S.prototype.triggerErr;
<commit_msg>canClose method added to streams.<commit_after>/**
 *

 *  Constructs a ProAct.Stream. The stream is a simple {@link ProAct.Actor}, without state.
 *

 *

 *  The streams are ment to emit values, events, changes and can be plugged into another actor.
 *  For example you can connect many streams, to merge them and to divide them, to plug them into properties.
 *

 *

 *  The reactive environment consists of the properties and the objects containing them, but
 *  the outside world is not reactive. It is possible to use the ProAct.Streams as connections from the
 *  outside world to the reactive environment.
 *

 *

 *    The transformations can be used to change the events or values emitetted.
 *

 *

 *  ProAct.Stream is part of the streams module of ProAct.js.
 *

 *
 * @class ProAct.Stream
 * @extends ProAct.Actor
 * @param {String} queueName
 *      The name of the queue all the updates should be pushed to.
 *

 *        If this parameter is null/undefined the default queue of
 *        {@link ProAct.flow} is used.
 *

 *

 *        If this parameter is not a string it is used as the
 *        source.
 *

 * @param {ProAct.Actor} source
 *      A default source of the stream, can be null.
 * @param {Array} transforms
 *      A list of transformation to be used on all incoming chages.
 */
function Stream (queueName, source, transforms) {
  if (queueName && !P.U.isString(queueName)) {
    transforms = source;
    source = queueName;
    queueName = null;
  }
  P.Actor.call(this, queueName, transforms);

  this.sourceNumber = 0;

  if (source) {
    this.into(source);
  }
}
ProAct.Stream = ProAct.S = Stream;

ProAct.Stream.prototype = P.U.ex(Object.create(P.Actor.prototype), {

  /**
   * Reference to the constructor of this object.
   *
   * @memberof ProAct.Stream
   * @instance
   * @constant
   * @type {Object}
   * @default ProAct.Stream
   */
  constructor: ProAct.Stream,

  /**
   * Creates the event to be send to the listeners on update.
   *

   *  Streams don't create new events by default, the event is the source.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeEvent
   * @param {ProAct.Event} source
   *      The source event of the event. It can be null
   * @return {ProAct.Event}
   *      The event.
   */
  makeEvent: function (source) {
    return source;
  },

  /**
   * Creates the listener of this stream.
   *

   *  The listener of the stream just calls the method {@link ProAct.Stream#trigger} with the incoming event/value.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeListener
   * @return {Object}
   *      The listener of this stream.
   */
  makeListener: function () {
    if (!this.listener) {
      var stream = this;
      this.listener = function (event) {
        stream.trigger(event, true);
      };
    }

    return this.listener;
  },

  /**
   * Creates the error listener of this stream.
   *

   *  The listener just calls {@link ProAct.Stream#triggerErr} of this with the incoming error.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeErrListener
   * @return {Object}
   *      The error listener of this stream.
   */
  makeErrListener: function () {
    if (!this.errListener) {
      var stream = this;
      this.errListener = function (error) {
        stream.triggerErr(error);
      };
    }

    return this.errListener;
  },

  /**
   * Creates the closing listener of this stream.
   *

   *  The listener just calls {@link ProAct.Stream#triggerClose} of this with the incoming closing data.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method makeCloseListener
   * @return {Object}
   *      The closing listener of this stream.
   */
  makeCloseListener: function () {
    if (!this.closeListener) {
      var stream = this;
      this.closeListener = function (error) {
        stream.triggerClose(error);
      };
    }

    return this.closeListener;
  },

  /**
   * Defers a ProAct.Actor listener.
   *

   *  For streams this means pushing it to active flow using {@link ProAct.Flow#push}.
   *  If the listener is object with 'property' field, it is done using {@link ProAct.Actor#defer}.
   *  That way the reactive environment is updated only once, but the streams are not part of it.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method defer
   * @param {Object} event
   *      The event/value to pass to the listener.
   * @param {Object} listener
   *      The listener to defer. It should be a function or object defining the call method.
   * @return {ProAct.Actor}
   *      this
   * @see {@link ProAct.Actor#willUpdate}
   * @see {@link ProAct.Actor#makeListener}
   * @see {@link ProAct.flow}
   */
  defer: function (event, listener) {
    if (!listener) {
      return;
    }

    if (listener.property) {
      P.Actor.prototype.defer.call(this, event, listener);
      return;
    }
    var queueName = (listener.queueName) ? listener.queueName : this.queueName;

    if (P.U.isFunction(listener)) {
      P.flow.push(queueName, listener, [event]);
    } else {
      P.flow.push(queueName, listener, listener.call, [event]);
    }
  },

  /**
   *

   *  Triggers a new event/value to the stream. Anything that is listening for events from
   *  this stream will get updated.
   *

   *

   *  ProAct.Stream.t is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method trigger
   * @param {Object} event
   *      The event/value to pass to trigger.
   * @param {Boolean} useTransformations
   *      If the stream should transform the triggered value. By default it is true (if not passed)
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  trigger: function (event, useTransformations) {
    if (useTransformations === undefined) {
      useTransformations = true;
    }

    return this.go(event, useTransformations);
  },

  /**
   *

   *  Triggers all the passed params, using transformations.
   *

   *

   *  ProAct.Stream.tt is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerMany
   * @param [...]
   *      A list of events/values to trigger
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Stream#trigger}
   */
  triggerMany: function () {
    var i, args = slice.call(arguments), ln = args.length;

    for (i = 0; i < ln; i++) {
      this.trigger(args[i], true);
    }

    return this;
  },

  /**
   *

   *  Triggers a new error to the stream. Anything that is listening for errors from
   *  this stream will get updated.
   *

   *

   *  ProAct.Stream.te is alias of this method.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerErr
   * @param {Error} err
   *      The error to trigger.
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  triggerErr: function (err) {
    return this.update(err, 'error');
  },

  /**
   *

   *  Triggers a closing event to the stream. Anything that is listening for closing events from
   *  this stream will get updated.
   *

   *

   *  The stream will be closed and unusable.
   *

   *
   * @memberof ProAct.Stream
   * @instance
   * @method triggerClose
   * @param {Object} data
   *      Data connected to the closing event.
   * @return {ProAct.Stream}
   *      this
   * @see {@link ProAct.Actor#update}
   */
  triggerClose: function (data) {
    return this.update(data, 'close');
  },

  // private
  go: function (event, useTransformations) {
    if (useTransformations) {
      try {
        event = P.Actor.transform(this, event);
      } catch (e) {
        this.triggerErr(e);
        return this;
      }
    }

    if (event === P.Actor.BadValue) {
      return this;
    }

    return this.update(event);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and mapping
   * the passed mapping function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method map
   * @param {Object} mappingFunction
   *      Function or object with a call method to use as map function.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the mapping applied.
   * @see {@link ProAct.Actor#mapping}
   */
  map: function (mappingFunction) {
    return new P.S(this).mapping(mappingFunction);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and filtering
   * the passed filtering function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method filter
   * @param {Object} filteringFunction
   *      The filtering function or object with a call method, should return boolean.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the filtering applied.
   * @see {@link ProAct.Actor#filtering}
   */
  filter: function (filteringFunction) {
    return new P.S(this).filtering(filteringFunction);
  },

  /**
   * Creates a new ProAct.Stream instance with source this and accumulation
   * the passed accumulation function.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method accumulate
   * @param {Object} initVal
   *      Initial value for the accumulation. For example '0' for sum.
   * @param {Object} accumulationFunction
   *      The function to accumulate.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the accumulation applied.
   * @see {@link ProAct.Actor#accumulation}
   */
  accumulate: function (initVal, accumulationFunction) {
    return new P.S(this).accumulation(initVal, accumulationFunction);
  },

  /**
   * Creates a new ProAct.Stream instance that merges this with other streams.
   * The new instance will have new value on value from any of the source streams.
   *
   * @memberof ProAct.Stream
   * @instance
   * @method merge
   * @param [...]
   *      A list of streams to be set as sources.
   * @return {ProAct.Stream}
   *      A new ProAct.Stream instance with the sources this and all the passed streams.
   */
  merge: function () {
    var sources = [this].concat(slice.call(arguments)),
        result = new P.S();

    return P.S.prototype.into.apply(result, sources);
  },

  into: function () {
    ProAct.Actor.prototype.into.apply(this, arguments);

    this.sourceNumber += arguments.length;

    return this;
  },

  canClose: function () {
    this.sourceNumber -= 1;

    return this.sourceNumber <= 0;
  }
});

P.U.ex(P.F.prototype, {

  /**
   * Retrieves the errStream for logging errors from this flow.
   * If there is no error stream, it is created.
   *
   * @memberof ProAct.Flow
   * @instance
   * @method errStream
   * @return {ProAct.Stream}
   *      The error stream of the flow.
   */
  errStream: function () {
    if (!this.errStreamVar) {
      this.errStreamVar = new P.S();
    }

    return this.errStreamVar;
  }
});

P.S.prototype.t = P.S.prototype.trigger;
P.S.prototype.tt = P.S.prototype.triggerMany;
P.S.prototype.te = P.S.prototype.triggerErr;
<|endoftext|>


<commit_before>(function () {
  /**
   * Returns an array of descendant elements that match the given tag names.
   * Tests: http://jsfiddle.net/tomalec/3u2CM/5/
   */
  function getFirstLevelChildTags(baseElement, tagNames) {
    var tagNo = tagNames.length;
    var children = [];
    var elements;
    while( tagNo-- ){
      elements = baseElement.getElementsByTagName(tagNames[ tagNo ]);
      elementLoop: for (var i = 0, ilen = elements.length; i < ilen; i++) {
        var elem = elements[i].parentNode;
        while (elem != baseElement) {
          if ( tagNames.indexOf(elem.nodeName )> -1 ) {
            continue elementLoop;
          }
          elem = elem.parentNode;
        }
        children.push(elements[i]);
      }
    }
    return children;
  }

  Polymer('juicy-tile-editor', {
    selectionMode: false,
    editedElement: null,
    highlightedElement: null,
    selectedItems: [],
    selectedElements: [],
    sortableTilesModel: null,
    mouseOverListener: null,
    mouseOutListener: null,
    mouseupListener: null,
    contextMenuListener: null,
    keyUpListener: null,
    tree: [],
    watchedTagNames: ["JUICY-TILE-LIST"],
    attached: function () {
      this.$.elementEdited.show(this.selectedElements.length ? this.selectedElements[0] : null);

      var that = this;
      this.$.treeView.toDisplayName = function(branch) {
        if(branch.node) { //juicy-tile-list root
          return branch.node.id;
        }
        else if (branch.name != void 0) { //container
          return branch.name;
        }
        else if (branch.index != void 0) { //element
          var txt = "";
          var elem = that.editedTiles.elements[ branch.index];
          var header = elem.querySelector("h1, h2, h3, h4, h5, h6");
          if(header) {
            txt = header.textContent;
          }
          else {
            txt = elem.textContent;
          }
          txt = txt.trim();
          if(!txt) {
            txt = "<" + elem.nodeName.toLowerCase() + ">";
          }
          if(txt.length > 23) {
            txt = txt.substr(0, 20) + " \u2026"; //'HORIZONTAL ELLIPSIS' (U+2026)
          }
          return txt;
        }
        else { //error
          return "Unnamed element"
        }
      }
    },
    detached: function () {
      this.$.elementEdited.hide();
      this.$.elementRollover.hide();
      this.$.elementSelected.hide();
      this.selectionMode = false;
      this.unlisten(); //changing property in "detached" callback does not execute "selectionModeChanged" (Polymer 0.2.3)
    },
    selectionModeChanged: function() {
      if (this.selectionMode) {
        this.listen();
      }
      else {
        this.unlisten();
      }
    },
    getSortableElement: function (elem) {
      while (elem.parentNode) {
        if (this.watchedTagNames.indexOf( elem.parentNode.nodeName ) > -1 ) {
          return elem;
        }
        elem = elem.parentNode;
      }
    },
    listen: function () {
      this.mouseOverListener = function (ev) {
        this.highlightedElement = null;
        var highlightedElement = this.getSortableElement(ev.target);
        if (highlightedElement) {
          if (this.highlightedElement !== highlightedElement) {
            this.highlightedElement = highlightedElement;
            this.$.elementRollover.show(this.highlightedElement);
          }
        }
      }.bind(this);

      this.mouseOutListener = function (ev) {
        this.$.elementRollover.hide();
      }.bind(this);

      this.mouseupListener = function (ev) {
        if (this.highlightedElement) {
          var model = this.highlightedElement.parentNode;
          this.treeRefresh();
          // ??? cantWe simply use editedElement?
          var highlightedItem = model.items[ model.elements.indexOf(this.highlightedElement) ];
          if (ev.ctrlKey || ev.metaKey || ev.shiftKey) {
            if(this.editedTiles == model) {
              //expand group
              var index = this.selectedItems.indexOf(highlightedItem);
              if(index == -1) {
                this.treeHighlightExtendAction(highlightedItem);
                this.$.treeView.highlightBranch(highlightedItem, true);
              }
              else {
                this.treeHighlightRemoveAction(highlightedItem);
                this.$.treeView.unhighlightBranch(highlightedItem);
              }
            }
          }
          else {
            this.treeHighlightAction(highlightedItem, model);
            this.$.treeView.highlightBranch(highlightedItem);
          }
          ev.preventDefault();
          ev.stopImmediatePropagation();
        }
      }.bind(this);

      this.clickListener = function (ev) {
        var sortableElement = this.getSortableElement(ev.target);
        if(sortableElement) {
          ev.preventDefault();
          ev.stopImmediatePropagation();
        }
      }.bind(this);

      this.contextMenuListener = function (ev) {
        if (ev.ctrlKey) {
          ev.preventDefault(); //on Mac, CTRL+Click opens system context menu, which we would like to avoid
        }
      }.bind(this);

      this.keyUpListener = function (ev) {
        if (ev.ctrlKey || ev.metaKey) { //mind that CTRL+T, CTRL+N, CTRL+W cannot be captured in Chrome
          if (ev.keyCode == 71) { //CTRL+G
            this.newGroupFromSelection();
            ev.preventDefault();
          }
          else if (ev.keyCode == 77) { //CTRL+M
            this.moveSelectionToEditedItemContainer();
            ev.preventDefault();
          }
          else if (ev.keyCode == 85) { //CTRL+U
            //TODO ungroup selection
          }
        }
      }.bind(this);

      window.addEventListener('mouseover', this.mouseOverListener);
      window.addEventListener('mouseout', this.mouseOutListener);
      window.addEventListener('mouseup', this.mouseupListener, true);
      window.addEventListener('mousedown', this.clickListener, true);
      window.addEventListener('click', this.clickListener, true);
      window.addEventListener('contextmenu', this.contextMenuListener);
      window.addEventListener('keyup', this.keyUpListener);
    },
    unlisten: function () {
      window.removeEventListener('mouseover', this.mouseOverListener);
      window.removeEventListener('mouseout', this.mouseOutListener);
      window.removeEventListener('mouseup', this.mouseupListener, true);
      window.removeEventListener('mousedown', this.clickListener, true);
      window.removeEventListener('click', this.clickListener, true);
      window.removeEventListener('contextmenu', this.contextMenuListener);
      window.removeEventListener('keyup', this.keyUpListener);
    },
    toggleSelectionMode: function () {
      this.selectionMode = !this.selectionMode;
    },
    getItemElement: function (item) {
    	//FIXME I may not work (tomalec)
      var model = this.editedTiles;
      if (item.name) {
        if (item.name === "root") {
          return model.$.container;
        }
        else {
          return model.elements[item.name];
        }
      }
      else {
        return model.elements[item.index];
      }
    },
    revertAction: function() {
      this.selectedItems.length = 0; //TODO solve this better (put changes on a stack?). Currently I need to clear selection because `this.editedTiles.loadFromStorage()` recreates `setup`, which results in `this.selectedItems` pointing to objects that are not referenced anymore [Marcin]
      this.$.elementEdited.show();
      this.$.elementRollover.hide();
      this.$.elementSelected.hide();
      this.treeChangedAction();
    },
    treeHighlightAction: function (item, tiles) {
      if(item.detail) {  //is tree event
        tiles = item.detail.tiles;
        item = item.detail.branch;
      }
      this.editedTiles = tiles;
      var element = this.getItemElement(item);
      this.$.elementEdited.show(element);
      this.selectedItems.length = 0;
      this.selectedItems.push(item);
      this.selectedElements.length = 0;
      this.selectedElements.push(element);
    },
    /**
     * [treeRefresh description]
     * @return {[type]} [description]
     */
    // tree: [
    // 	{
    // 		node: _juicy-tile-list_,
    // 		setup: _PackageSetup_, // redundand consider removal
    // 		branches: [
    // 			_setup.items[?].index_: [
    // 				_tree_,
    // 				_tree_
    // 			]
    // 		]
    // 	}
    // ]
    treeRefresh: function() {
      var that = this,
      	  tree = [];
      var extendWithSubTiles = function (element, parentJuicyTile) {
        var nested,
        	branches = [];

        // iterate on element's real DOM elements
        for (var childNo = 0, eLen = element.elements.length; childNo < eLen; childNo++) {
          if ( that.watchedTagNames.indexOf( element.elements[childNo].nodeName ) > -1 ) { //element is directly a nested tiles
            nested = [element.elements[childNo]]
          }
          else { //check if element has nested tiles children
            nested = getFirstLevelChildTags(element.elements[childNo], that.watchedTagNames);
          }
        	// iterate on nested juicy-tile-list
        	if( nested.length ){
        		branches[ childNo ] = [];

          	for(var branchNo = 0, bLen = nested.length; branchNo < bLen; branchNo++){
          		branches[ childNo ].push(
          			extendWithSubTiles(
          				nested[branchNo],
          				element.elements[childNo]
          			)
          		);
          	}
          }
        }

        return {
        	node: element,
        	setup: element.setup, // redundand consider removal
        	branches: branches
        }
      }

      //find all trees
      var topmost = this;
      while (topmost.parentNode) {
        topmost = topmost.parentNode;
      }

      var models = getFirstLevelChildTags(topmost, this.watchedTagNames);

      if (topmost.host) {
        var distributedModels = getFirstLevelChildTags(topmost.host, this.watchedTagNames);
        models = models.concat(distributedModels);
      }

      for (var i = 0, ilen = models.length; i < ilen; i++) {
        tree.push(
          extendWithSubTiles(models[i])
        );
      }
      // notify observer/two-way-binding/tempalte only once
      this.tree = tree;
    },
    treeHighlightExtendAction: function(item) {
      if(item.detail) {  //is tree event
        item = item.detail.branch;
      }
      this.selectedItems.push(item);
      var elem = this.editedTiles.elements[item.name || item.index];
      this.selectedElements.push(elem);
      this.$.elementSelected.show(this.selectedElements);
    },
    treeHighlightRemoveAction: function(item) {
      if(item.detail) {  //is tree event
        item = item.detail.branch;
      }
      var index = this.selectedItems.indexOf(item);
      this.selectedItems.splice(index, 1);
      this.selectedElements.splice(index, 1);
      this.$.elementSelected.show(this.selectedElements);
    },
    treeChangedAction: function() {
      this.treeRefresh();
      this.$.treeView.highlightBranch(this.selectedItems);
    }
  });
})();<commit_msg>Attach event listeners to <juicy-tile-list>s instead of window<commit_after>(function () {

  /**
   * Returns an array of descendant elements that match the given tag names.
   * Tests: http://jsfiddle.net/tomalec/3u2CM/5/
   */
  function getFirstLevelChildTags(baseElement, tagNames) {
    var tagNo = tagNames.length;
    var children = [];
    var elements;
    while( tagNo-- ){
      elements = baseElement.getElementsByTagName(tagNames[ tagNo ]);
      elementLoop: for (var i = 0, ilen = elements.length; i < ilen; i++) {
        var elem = elements[i].parentNode;
        while (elem != baseElement) {
          if ( tagNames.indexOf(elem.nodeName )> -1 ) {
            continue elementLoop;
          }
          elem = elem.parentNode;
        }
        children.push(elements[i]);
      }
    }
    return children;
  }

  function getChildOfContaining( parent, node ){
    var elem = node;
    while( elem && elem.parentNode != parent ){
      elem = elem.parentNode;
    }
    return elem;
  }

  Polymer('juicy-tile-editor', {
    selectionMode: false,
    editedElement: null,
    highlightedElement: null,
    selectedItems: [],
    selectedElements: [],
    sortableTilesModel: null,
    mouseOverListener: null,
    mouseOutListener: null,
    mouseupListener: null,
    contextMenuListener: null,
    keyUpListener: null,
    tree: [],
    /** NodeList of <juicy-tile-list> elements we will bind to */
    tileLists: null,
    watchedTagNames: ["JUICY-TILE-LIST"],
    attached: function () {
      // getElementsByTagName is cool because it's fast and its LIVE
      // as it is live, consider moving to created callback.
      this.tileLists = this.ownerDocument.getElementsByTagName('juicy-tile-list');

      this.$.elementEdited.show(this.selectedElements.length ? this.selectedElements[0] : null);

      var that = this;
      this.$.treeView.toDisplayName = function(branch) {
        if(branch.node) { //juicy-tile-list root
          return branch.node.id;
        }
        else if (branch.name != void 0) { //container
          return branch.name;
        }
        else if (branch.index != void 0) { //element
          var txt = "";
          var elem = that.editedTiles.elements[ branch.index];
          var header = elem.querySelector("h1, h2, h3, h4, h5, h6");
          if(header) {
            txt = header.textContent;
          }
          else {
            txt = elem.textContent;
          }
          txt = txt.trim();
          if(!txt) {
            txt = "<" + elem.nodeName.toLowerCase() + ">";
          }
          if(txt.length > 23) {
            txt = txt.substr(0, 20) + " \u2026"; //'HORIZONTAL ELLIPSIS' (U+2026)
          }
          return txt;
        }
        else { //error
          return "Unnamed element"
        }
      }
    },
    detached: function () {
      this.$.elementEdited.hide();
      this.$.elementRollover.hide();
      this.$.elementSelected.hide();
      this.selectionMode = false;
      this.unlisten(); //changing property in "detached" callback does not execute "selectionModeChanged" (Polymer 0.2.3)
    },
    selectionModeChanged: function() {
      if (this.selectionMode) {
        this.listen();
      }
      else {
        this.unlisten();
      }
    },
    getSortableElement: function (elem) {
      while (elem.parentNode) {
        if (this.watchedTagNames.indexOf( elem.parentNode.nodeName ) > -1 ) {
          return elem;
        }
        elem = elem.parentNode;
      }
    },
    listen: function () {
      var editor = this;
      // Highlight hovered tile
      this.mouseOverListener = function (ev) {
        editor.highlightedElement = null;

        var highlightedElement = getChildOfContaining(this, ev.target);
        if (highlightedElement) {
          if (editor.highlightedElement !== highlightedElement) {
            editor.highlightedElement = highlightedElement;
            editor.$.elementRollover.show( highlightedElement);
          }
        }
      }
      // Remove highlight
      this.mouseOutListener = function (ev) {
        this.$.elementRollover.hide();
      }.bind(this);

      // Attach clicked tile for editing
      // Expand selection if cmd/ctrl/shift button pressed
      this.mouseupListener = function (ev) {
        if (this.highlightedElement) {
          var model = this.highlightedElement.parentNode;
          this.treeRefresh();
          // ??? cantWe simply use editedElement?
          var highlightedItem = model.items[ model.elements.indexOf(this.highlightedElement) ];
          if (ev.ctrlKey || ev.metaKey || ev.shiftKey) {
            if(this.editedTiles == model) {
              //expand group
              var index = this.selectedItems.indexOf(highlightedItem);
              if(index == -1) {
                this.treeHighlightExtendAction(highlightedItem);
                this.$.treeView.highlightBranch(highlightedItem, true);
              }
              else {
                this.treeHighlightRemoveAction(highlightedItem);
                this.$.treeView.unhighlightBranch(highlightedItem);
              }
            }
          }
          else {
            this.treeHighlightAction(highlightedItem, model);
            this.$.treeView.highlightBranch(highlightedItem);
          }
          ev.preventDefault();
          ev.stopImmediatePropagation();
        }
      }.bind(this);

      // Block clicking on tile content over highlight
      this.clickListener = function (ev) {
        var sortableElement = this.getSortableElement(ev.target);
        if(sortableElement) {
          ev.preventDefault();
          ev.stopImmediatePropagation();
        }
      }.bind(this);
      // Mac command kay fix
      this.contextMenuListener = function (ev) {
        if (ev.ctrlKey) {
          ev.preventDefault(); //on Mac, CTRL+Click opens system context menu, which we would like to avoid
        }
      }.bind(this);
      // Shortcuts
      this.keyUpListener = function (ev) {
        if (ev.ctrlKey || ev.metaKey) { //mind that CTRL+T, CTRL+N, CTRL+W cannot be captured in Chrome
          if (ev.keyCode == 71) { //CTRL+G
            this.newGroupFromSelection();
            ev.preventDefault();
          }
          else if (ev.keyCode == 77) { //CTRL+M
            this.moveSelectionToEditedItemContainer();
            ev.preventDefault();
          }
          else if (ev.keyCode == 85) { //CTRL+U
            //TODO ungroup selection
          }
        }
      }.bind(this);

      // attach listeners for every <juicy-tile-list>
      var listNo = this.tileLists.length;
      var list;
      while( listNo-- ){
        list = this.tileLists[ listNo ];

        list.addEventListener('mouseover', this.mouseOverListener);
        // list.addEventListener('mouseout', this.mouseOutListener);
        // list.addEventListener('mouseup', this.mouseupListener, true);
        // list.addEventListener('mousedown', this.clickListener, true);
        // list.addEventListener('click', this.clickListener, true);
        // list.addEventListener('contextmenu', this.contextMenuListener);
        // list.addEventListener('keyup', this.keyUpListener);
      }

      window.addEventListener('mouseout', this.mouseOutListener);
      window.addEventListener('mouseup', this.mouseupListener, true);
      window.addEventListener('mousedown', this.clickListener, true);
      window.addEventListener('click', this.clickListener, true);
      window.addEventListener('contextmenu', this.contextMenuListener);
      window.addEventListener('keyup', this.keyUpListener);
    },
    unlisten: function () {
      // remove listeners for every <juicy-tile-list>
      var listNo = this.tileLists.length;
      var list;
      while( listNo-- ){
        list = this.tileLists[ listNo ];

        list.removeEventListener('mouseover', this.mouseOverListener);
        // list.removeEventListener('mouseout', this.mouseOutListener);
        // list.removeEventListener('mouseup', this.mouseupListener, true);
        // list.removeEventListener('mousedown', this.clickListener, true);
        // list.removeEventListener('click', this.clickListener, true);
        // list.removeEventListener('contextmenu', this.contextMenuListener);
        // list.removeEventListener('keyup', this.keyUpListener);
      }
      window.removeEventListener('mouseover', this.mouseOverListener);
      window.removeEventListener('mouseout', this.mouseOutListener);
      window.removeEventListener('mouseup', this.mouseupListener, true);
      window.removeEventListener('mousedown', this.clickListener, true);
      window.removeEventListener('click', this.clickListener, true);
      window.removeEventListener('contextmenu', this.contextMenuListener);
      window.removeEventListener('keyup', this.keyUpListener);
    },
    toggleSelectionMode: function () {
      this.selectionMode = !this.selectionMode;
    },
    getItemElement: function (item) {
      //FIXME I may not work (tomalec)
      var model = this.editedTiles;
      if (item.name) {
        if (item.name === "root") {
          return model.$.container;
        }
        else {
          return model.elements[item.name];
        }
      }
      else {
        return model.elements[item.index];
      }
    },
    revertAction: function() {
      this.selectedItems.length = 0; //TODO solve this better (put changes on a stack?). Currently I need to clear selection because `this.editedTiles.loadFromStorage()` recreates `setup`, which results in `this.selectedItems` pointing to objects that are not referenced anymore [Marcin]
      this.$.elementEdited.show();
      this.$.elementRollover.hide();
      this.$.elementSelected.hide();
      this.treeChangedAction();
    },
    treeHighlightAction: function (item, tiles) {
      if(item.detail) {  //is tree event
        tiles = item.detail.tiles;
        item = item.detail.branch;
      }
      this.editedTiles = tiles;
      var element = this.getItemElement(item);
      this.$.elementEdited.show(element);
      this.selectedItems.length = 0;
      this.selectedItems.push(item);
      this.selectedElements.length = 0;
      this.selectedElements.push(element);
    },
    /**
     * [treeRefresh description]
     * @return {[type]} [description]
     */
    // tree: [
    //  {
    //    node: _juicy-tile-list_,
    //    setup: _PackageSetup_, // redundand consider removal
    //    branches: [
    //      _setup.items[?].index_: [
    //        _tree_,
    //        _tree_
    //      ]
    //    ]
    //  }
    // ]
    treeRefresh: function() {
      var that = this,
          tree = [];
      var extendWithSubTiles = function (element, parentJuicyTile) {
        var nested,
          branches = [];

        // iterate on element's real DOM elements
        for (var childNo = 0, eLen = element.elements.length; childNo < eLen; childNo++) {
          if ( that.watchedTagNames.indexOf( element.elements[childNo].nodeName ) > -1 ) { //element is directly a nested tiles
            nested = [element.elements[childNo]]
          }
          else { //check if element has nested tiles children
            nested = getFirstLevelChildTags(element.elements[childNo], that.watchedTagNames);
          }
          // iterate on nested juicy-tile-list
          if( nested.length ){
            branches[ childNo ] = [];

              for(var branchNo = 0, bLen = nested.length; branchNo < bLen; branchNo++){
                branches[ childNo ].push(
                  extendWithSubTiles(
                    nested[branchNo],
                    element.elements[childNo]
                  )
                );
              }
            }
        }

        return {
          node: element,
          setup: element.setup, // redundand consider removal
          branches: branches
        }
      }

      //find all trees
      var topmost = this;
      while (topmost.parentNode) {
        topmost = topmost.parentNode;
      }

      var models = getFirstLevelChildTags(topmost, this.watchedTagNames);

      if (topmost.host) {
        var distributedModels = getFirstLevelChildTags(topmost.host, this.watchedTagNames);
        models = models.concat(distributedModels);
      }

      for (var i = 0, ilen = models.length; i < ilen; i++) {
        tree.push(
          extendWithSubTiles(models[i])
        );
      }
      // notify observer/two-way-binding/tempalte only once
      this.tree = tree;
    },
    treeHighlightExtendAction: function(item) {
      if(item.detail) {  //is tree event
        item = item.detail.branch;
      }
      this.selectedItems.push(item);
      var elem = this.editedTiles.elements[item.name || item.index];
      this.selectedElements.push(elem);
      this.$.elementSelected.show(this.selectedElements);
    },
    treeHighlightRemoveAction: function(item) {
      if(item.detail) {  //is tree event
        item = item.detail.branch;
      }
      var index = this.selectedItems.indexOf(item);
      this.selectedItems.splice(index, 1);
      this.selectedElements.splice(index, 1);
      this.$.elementSelected.show(this.selectedElements);
    },
    treeChangedAction: function() {
      this.treeRefresh();
      this.$.treeView.highlightBranch(this.selectedItems);
    }
  });
})();<|endoftext|>


<commit_before>/*
Language: Haskell
Author: Jeremy Hull <sourdrums@gmail.com>
Contributors: Zena Treep <zena.treep@gmail.com>
Category: functional
*/

function(hljs) {
  var COMMENT = {
    variants: [
      hljs.COMMENT('--', '$'),
      hljs.COMMENT(
        '{-',
        '-}',
        {
          contains: ['self']
        }
      )
    ]
  };

  var PRAGMA = {
    className: 'meta',
    begin: '{-#', end: '#-}'
  };

  var PREPROCESSOR = {
    className: 'meta',
    begin: '^#', end: '$'
  };

  var CONSTRUCTOR = {
    className: 'type',
    begin: '\\b[A-Z][\\w\']*', // TODO: other constructors (build-in, infix).
    relevance: 0
  };

  var LIST = {
    begin: '\\(', end: '\\)',
    illegal: '"',
    contains: [
      PRAGMA,
      PREPROCESSOR,
      {className: 'type', begin: '\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?'},
      hljs.inherit(hljs.TITLE_MODE, {begin: '[_a-z][\\w\']*'}),
      COMMENT
    ]
  };

  var RECORD = {
    begin: '{', end: '}',
    contains: LIST.contains
  };

  return {
    aliases: ['hs'],
    keywords:
      'let in if then else case of where do module import hiding ' +
      'qualified type data newtype deriving class instance as default ' +
      'infix infixl infixr foreign export ccall stdcall cplusplus ' +
      'jvm dotnet safe unsafe family forall mdo proc rec',
    contains: [

      // Top-level constructions.

      {
        begin: '\\bmodule\\b', end: 'where',
        keywords: 'module where',
        contains: [LIST, COMMENT],
        illegal: '\\W\\.|;'
      },
      {
        begin: '\\bimport\\b', end: '$',
        keywords: 'import|0 qualified as hiding',
        contains: [LIST, COMMENT],
        illegal: '\\W\\.|;'
      },

      {
        className: 'class',
        begin: '^(\\s*)?(class|instance)\\b', end: 'where',
        keywords: 'class family instance where',
        contains: [CONSTRUCTOR, LIST, COMMENT]
      },
      {
        className: 'class',
        begin: '\\b(data|(new)?type)\\b', end: '$',
        keywords: 'data family type newtype deriving',
        contains: [PRAGMA, CONSTRUCTOR, LIST, RECORD, COMMENT]
      },
      {
        beginKeywords: 'default', end: '$',
        contains: [CONSTRUCTOR, LIST, COMMENT]
      },
      {
        beginKeywords: 'infix infixl infixr', end: '$',
        contains: [hljs.C_NUMBER_MODE, COMMENT]
      },
      {
        begin: '\\bforeign\\b', end: '$',
        keywords: 'foreign import export ccall stdcall cplusplus jvm ' +
                  'dotnet safe unsafe',
        contains: [CONSTRUCTOR, hljs.QUOTE_STRING_MODE, COMMENT]
      },
      {
        className: 'meta',
        begin: '#!\\/usr\\/bin\\/env\ runhaskell', end: '$'
      },

      // "Whitespaces".

      PRAGMA,
      PREPROCESSOR,

      // Literals and names.

      // TODO: characters.
      hljs.QUOTE_STRING_MODE,
      hljs.C_NUMBER_MODE,
      CONSTRUCTOR,
      hljs.inherit(hljs.TITLE_MODE, {begin: '^[_a-z][\\w\']*'}),

      COMMENT,

      {begin: '->|<-'} // No markup, relevance booster
    ]
  };
}
<commit_msg>Fixed cross-detection problem between Elm and Haskell.<commit_after>/*
Language: Haskell
Author: Jeremy Hull <sourdrums@gmail.com>
Contributors: Zena Treep <zena.treep@gmail.com>
Category: functional
*/

function(hljs) {
  var COMMENT = {
    variants: [
      hljs.COMMENT('--', '$'),
      hljs.COMMENT(
        '{-',
        '-}',
        {
          contains: ['self']
        }
      )
    ]
  };

  var PRAGMA = {
    className: 'meta',
    begin: '{-#', end: '#-}'
  };

  var PREPROCESSOR = {
    className: 'meta',
    begin: '^#', end: '$'
  };

  var CONSTRUCTOR = {
    className: 'type',
    begin: '\\b[A-Z][\\w\']*', // TODO: other constructors (build-in, infix).
    relevance: 0
  };

  var LIST = {
    begin: '\\(', end: '\\)',
    illegal: '"',
    contains: [
      PRAGMA,
      PREPROCESSOR,
      {className: 'type', begin: '\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?'},
      hljs.inherit(hljs.TITLE_MODE, {begin: '[_a-z][\\w\']*'}),
      COMMENT
    ]
  };

  var RECORD = {
    begin: '{', end: '}',
    contains: LIST.contains
  };

  return {
    aliases: ['hs'],
    keywords:
      'let in if then else case of where do module import hiding ' +
      'qualified type data newtype deriving class instance as default ' +
      'infix infixl infixr foreign export ccall stdcall cplusplus ' +
      'jvm dotnet safe unsafe family forall mdo proc rec',
    contains: [

      // Top-level constructions.

      {
        beginKeywords: 'module', end: 'where',
        keywords: 'module where',
        contains: [LIST, COMMENT],
        illegal: '\\W\\.|;'
      },
      {
        begin: '\\bimport\\b', end: '$',
        keywords: 'import qualified as hiding',
        contains: [LIST, COMMENT],
        illegal: '\\W\\.|;'
      },

      {
        className: 'class',
        begin: '^(\\s*)?(class|instance)\\b', end: 'where',
        keywords: 'class family instance where',
        contains: [CONSTRUCTOR, LIST, COMMENT]
      },
      {
        className: 'class',
        begin: '\\b(data|(new)?type)\\b', end: '$',
        keywords: 'data family type newtype deriving',
        contains: [PRAGMA, CONSTRUCTOR, LIST, RECORD, COMMENT]
      },
      {
        beginKeywords: 'default', end: '$',
        contains: [CONSTRUCTOR, LIST, COMMENT]
      },
      {
        beginKeywords: 'infix infixl infixr', end: '$',
        contains: [hljs.C_NUMBER_MODE, COMMENT]
      },
      {
        begin: '\\bforeign\\b', end: '$',
        keywords: 'foreign import export ccall stdcall cplusplus jvm ' +
                  'dotnet safe unsafe',
        contains: [CONSTRUCTOR, hljs.QUOTE_STRING_MODE, COMMENT]
      },
      {
        className: 'meta',
        begin: '#!\\/usr\\/bin\\/env\ runhaskell', end: '$'
      },

      // "Whitespaces".

      PRAGMA,
      PREPROCESSOR,

      // Literals and names.

      // TODO: characters.
      hljs.QUOTE_STRING_MODE,
      hljs.C_NUMBER_MODE,
      CONSTRUCTOR,
      hljs.inherit(hljs.TITLE_MODE, {begin: '^[_a-z][\\w\']*'}),

      COMMENT,

      {begin: '->|<-'} // No markup, relevance booster
    ]
  };
}
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 * Tile QT +0.7.x format
 * http://www.mapeditor.org/
 *
 */
(function () {

    // constant to identify the collision object layer
    var COLLISION_GROUP = "collision";

    /**
     * set a compatible renderer object
     * for the specified map
     * @ignore
     */
    function getNewDefaultRenderer(obj) {
        switch (obj.orientation) {
            case "orthogonal":
                return new me.TMXOrthogonalRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight
                );

            case "isometric":
                return new me.TMXIsometricRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight
                );

            case "hexagonal":
                return new me.TMXHexagonalRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight,
                    obj.hexsidelength,
                    obj.staggeraxis,
                    obj.staggerindex
                );

            // if none found, throw an exception
            default:
                throw new me.Error(obj.orientation + " type TMX Tile Map not supported!");
        }
    }

    /**
     * Set tiled layer Data
     * @ignore
     */
    function setLayerData(layer, data) {
        var idx = 0;
        // set everything
        for (var y = 0 ; y < layer.rows; y++) {
            for (var x = 0; x < layer.cols; x++) {
                // get the value of the gid
                var gid = data[idx++];
                // fill the array
                if (gid !== 0) {
                    // add a new tile to the layer
                    layer.setTile(x, y, gid);
                }
            }
        }
    }

    /**
     * read the layer Data
     * @ignore
     */
    function readLayer(map, data, z) {
        var layer = new me.TMXLayer(map.tilewidth, map.tileheight, map.orientation, map.tilesets, z);
        // init the layer properly
        layer.initFromJSON(data);
        // set a renderer
        if (!me.game.tmxRenderer.canRender(layer)) {
            layer.setRenderer(getNewDefaultRenderer(layer));
        }
        else {
            // use the default one
            layer.setRenderer(me.game.tmxRenderer);
        }
        // parse the layer data
        setLayerData(layer,
            me.TMXUtils.decode(
                data.data,
                data.encoding,
                data.compression
            )
        );
        return layer;
    }

    /**
     * read the Image Layer Data
     * @ignore
     */
    function readImageLayer(map, data, z) {
        // Normalize properties
        me.TMXUtils.applyTMXProperties(data.properties, data);

        // create the layer
        var imageLayer = new me.ImageLayer(
            +data.x || 0,
            +data.y || 0,
            Object.assign({
                name: data.name,
                image: data.image,
                z: z
            }, data.properties)
        );

        // set some additional flags
        var visible = typeof(data.visible) !== "undefined" ? data.visible : true;
        imageLayer.setOpacity(visible ? +data.opacity : 0);

        return imageLayer;
    }

    /**
     * read the tileset Data
     * @ignore
     */
    function readTileset(data) {
        return (new me.TMXTileset(data));
    }

    /**
     * read the object group Data
     * @ignore
     */
    function readObjectGroup(map, data, z) {
        return (new me.TMXObjectGroup(data.name, data, map.orientation, map.tilesets, z));
    }

    /**
     * a TMX Tile Map Object
     * Tiled QT +0.7.x format
     * @class
     * @extends me.Object
     * @memberOf me
     * @constructor
     * @param {String} levelId name of TMX map
     * @param {Object} data TMX map in JSON format
     * @example
     * // create a new level object based on the TMX JSON object
     * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
     * // add the level to the game world container
     * level.addTo(me.game.world, true);
     */
    me.TMXTileMap = me.Object.extend({
        // constructor
        init: function (levelId, data) {

            /**
             * name of the tilemap
             * @public
             * @type String
             * @name me.TMXTileMap#name
             */
            this.name = levelId;

            /**
             * the level data (JSON)
             * @ignore
             */
            this.data = data;

            /**
             * width of the tilemap in tiles
             * @public
             * @type Int
             * @name me.TMXTileMap#cols
             */
            this.cols = +data.width;
            /**
             * height of the tilemap in tiles
             * @public
             * @type Int
             * @name me.TMXTileMap#rows
             */
            this.rows = +data.height;

            /**
             * Tile width
             * @public
             * @type Int
             * @name me.TMXTileMap#tilewidth
             */
            this.tilewidth = +data.tilewidth;

            /**
             * Tile height
             * @public
             * @type Int
             * @name me.TMXTileMap#tileheight
             */
            this.tileheight = +data.tileheight;

            // tilesets for this map
            this.tilesets = null;
            // layers
            this.layers = [];
            // group objects
            this.objectGroups = [];

            // tilemap version
            this.version = data.version;

            // map type (orthogonal or isometric)
            this.orientation = data.orientation;
            if (this.orientation === "isometric") {
                this.width = (this.cols + this.rows) * (this.tilewidth / 2);
                this.height = (this.cols + this.rows) * (this.tileheight / 2);
            } else {
                this.width = this.cols * this.tilewidth;
                this.height = this.rows * this.tileheight;
            }

            // objects minimum z order
            this.z = 0;

            // object id
            this.nextobjectid = +data.nextobjectid || undefined;

            // hex/iso properties
            this.hexsidelength = +data.hexsidelength || undefined;
            this.staggeraxis = data.staggeraxis;
            this.staggerindex = data.staggerindex;

            // background color
            this.backgroundcolor = data.backgroundcolor;

            // set additional map properties (if any)
            me.TMXUtils.applyTMXProperties(this, data);

            // initialize a default TMX renderer
            if ((me.game.tmxRenderer === null) || !me.game.tmxRenderer.canRender(this)) {
                me.game.tmxRenderer = getNewDefaultRenderer(this);
            }

            // internal flag
            this.initialized = false;

        },

        /**
         * parse the map
         * @ignore
         */
        readMapObjects: function (data) {

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

            // to automatically increment z index
            var zOrder = this.z;
            var self = this;

            // Tileset information
            if (!this.tilesets) {
                // make sure we have a TilesetGroup Object
                this.tilesets = new me.TMXTilesetGroup();
            }

            // parse all tileset objects
            var tilesets = data.tilesets;
            tilesets.forEach(function (tileset) {
                // add the new tileset
                self.tilesets.add(readTileset(tileset));
            });

            // check if a user-defined background color is defined
            if (this.backgroundcolor) {
                this.layers.push(
                    new me.ColorLayer(
                        "background_color",
                        this.backgroundcolor,
                        zOrder++
                    )
                );
            }

            // check if a background image is defined
            if (this.background_image) {
                // add a new image layer
                this.layers.push(new me.ImageLayer(
                    0, 0, {
                        name : "background_image",
                        image : this.background_image,
                        z : zOrder++
                    }
                ));
            }

            data.layers.forEach(function (layer) {
                switch (layer.type) {
                    case "imagelayer":
                        self.layers.push(readImageLayer(self, layer, zOrder++));
                        break;

                    case "tilelayer":
                        self.layers.push(readLayer(self, layer, zOrder++));
                        break;

                    // get the object groups information
                    case "objectgroup":
                        self.objectGroups.push(readObjectGroup(self, layer, zOrder++));
                        break;

                    default:
                        break;
                }
            });
            this.initialized = true;
        },

        /**
         * add all the map layers and objects to the given container
         * @name me.TMXTileMap#addTo
         * @public
         * @function
         * @param {me.Container} target container
         * @param {boolean} flatten if true, flatten all objects into the given container
         * @example
         * // create a new level object based on the TMX JSON object
         * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
         * // add the level to the game world container
         * level.addTo(me.game.world, true);
         */
        addTo : function (container, flatten) {
            var _sort = container.autoSort;
            var _depth = container.autoDepth;

            // disable auto-sort and auto-depth
            container.autoSort = false;
            container.autoDepth = false;

            // add all layers instances
            this.getLayers().forEach(function (layer) {
                container.addChild(layer);
            });

            // add all Object instances
            this.getObjects(flatten).forEach(function (object) {
                container.addChild(object);
            });

            //  set back auto-sort and auto-depth
            container.autoSort = _sort;
            container.autoDepth = _depth;

            // force a sort
            container.sort(true);
        },

        /**
         * return an Array of instantiated objects, based on the map object definition
         * @name me.TMXTileMap#getObjects
         * @public
         * @function
         * @param {boolean} flatten if true, flatten all objects into the returned array, <br>
         * ignoring all defined groups (no sub containers will be created)
         * @return {me.Renderable[]} Array of Objects
         */
        getObjects : function (flatten) {
            var objects = [];
            var isCollisionGroup = false;
            var targetContainer;

            // parse the map for objects
            this.readMapObjects(this.data);

            for (var g = 0; g < this.objectGroups.length; g++) {
                var group = this.objectGroups[g];

                // check if this is the collision shape group
                isCollisionGroup = group.name.toLowerCase().includes(COLLISION_GROUP);

                if (flatten === false) {
                    // create a new container
                    targetContainer = new me.Container(0, 0, this.width, this.height);

                    // set additional properties
                    targetContainer.name = group.name;
                    targetContainer.pos.z = group.z;
                    targetContainer.setOpacity(group.opacity);

                    // disable auto-sort and auto-depth
                    targetContainer.autoSort = false;
                    targetContainer.autoDepth = false;
                }

                // iterate through the group and add all object into their
                // corresponding target Container
                for (var o = 0; o < group.objects.length; o++) {
                    // TMX object settings
                    var settings = group.objects[o];

                    var obj = me.pool.pull(
                        settings.name || "me.Entity",
                        settings.x, settings.y,
                        settings
                    );
                    // skip if the pull function does not return a corresponding object
                    if (typeof obj !== "object") {
                        continue;
                    }

                    // check if a me.Tile object is embedded
                    if (typeof (settings.tile) === "object" && !obj.renderable) {
                        obj.renderable = settings.tile.getRenderable(settings);
                    }

                    if (isCollisionGroup && !settings.name) {
                        // configure the body accordingly
                        obj.body.collisionType = me.collision.types.WORLD_SHAPE;
                    }

                    // set the obj z order correspondingly to its parent container/group
                    obj.pos.z = group.z;

                    //apply group opacity value to the child objects if group are merged
                    if (flatten === true) {
                        if (obj.isRenderable === true) {
                            obj.setOpacity(obj.getOpacity() * group.opacity);
                            // and to child renderables if any
                            if (obj.renderable instanceof me.Renderable) {
                                obj.renderable.setOpacity(obj.renderable.getOpacity() * group.opacity);
                            }
                        }
                        // directly add the obj into the objects array
                        objects.push(obj);
                    } else /* false*/ {
                        // add it to the new container
                        targetContainer.addChild(obj);
                    }

                }

                // if we created a new container
                if ((flatten === false) && (targetContainer.children.length > 0)) {

                    // re-enable auto-sort and auto-depth
                    targetContainer.autoSort = true;
                    targetContainer.autoDepth = true;

                    // add our container to the world
                    objects.push(targetContainer);
                }
            }
            return objects;
        },

        /**
         * return all the existing layers
         * @name me.TMXTileMap#getLayers
         * @public
         * @function
         * @return {me.TMXLayer[]} Array of Layers
         */
        getLayers : function () {
            // parse the map for objects
            this.readMapObjects(this.data);
            return this.layers;
        },

        /**
         * destroy function, clean all allocated objects
         * @name me.TMXTileMap#destroy
         * @public
         * @function
         */
        destroy : function () {
            this.tilesets = undefined;
            this.layers = [];
            this.objectGroups = [];
            this.initialized = false;
        }
    });
})();
<commit_msg>fix a regression with level loading when no tileset are defined.<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 * Tile QT +0.7.x format
 * http://www.mapeditor.org/
 *
 */
(function () {

    // constant to identify the collision object layer
    var COLLISION_GROUP = "collision";

    /**
     * set a compatible renderer object
     * for the specified map
     * @ignore
     */
    function getNewDefaultRenderer(obj) {
        switch (obj.orientation) {
            case "orthogonal":
                return new me.TMXOrthogonalRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight
                );

            case "isometric":
                return new me.TMXIsometricRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight
                );

            case "hexagonal":
                return new me.TMXHexagonalRenderer(
                    obj.cols,
                    obj.rows,
                    obj.tilewidth,
                    obj.tileheight,
                    obj.hexsidelength,
                    obj.staggeraxis,
                    obj.staggerindex
                );

            // if none found, throw an exception
            default:
                throw new me.Error(obj.orientation + " type TMX Tile Map not supported!");
        }
    }

    /**
     * Set tiled layer Data
     * @ignore
     */
    function setLayerData(layer, data) {
        var idx = 0;
        // set everything
        for (var y = 0 ; y < layer.rows; y++) {
            for (var x = 0; x < layer.cols; x++) {
                // get the value of the gid
                var gid = data[idx++];
                // fill the array
                if (gid !== 0) {
                    // add a new tile to the layer
                    layer.setTile(x, y, gid);
                }
            }
        }
    }

    /**
     * read the layer Data
     * @ignore
     */
    function readLayer(map, data, z) {
        var layer = new me.TMXLayer(map.tilewidth, map.tileheight, map.orientation, map.tilesets, z);
        // init the layer properly
        layer.initFromJSON(data);
        // set a renderer
        if (!me.game.tmxRenderer.canRender(layer)) {
            layer.setRenderer(getNewDefaultRenderer(layer));
        }
        else {
            // use the default one
            layer.setRenderer(me.game.tmxRenderer);
        }
        // parse the layer data
        setLayerData(layer,
            me.TMXUtils.decode(
                data.data,
                data.encoding,
                data.compression
            )
        );
        return layer;
    }

    /**
     * read the Image Layer Data
     * @ignore
     */
    function readImageLayer(map, data, z) {
        // Normalize properties
        me.TMXUtils.applyTMXProperties(data.properties, data);

        // create the layer
        var imageLayer = new me.ImageLayer(
            +data.x || 0,
            +data.y || 0,
            Object.assign({
                name: data.name,
                image: data.image,
                z: z
            }, data.properties)
        );

        // set some additional flags
        var visible = typeof(data.visible) !== "undefined" ? data.visible : true;
        imageLayer.setOpacity(visible ? +data.opacity : 0);

        return imageLayer;
    }

    /**
     * read the tileset Data
     * @ignore
     */
    function readTileset(data) {
        return (new me.TMXTileset(data));
    }

    /**
     * read the object group Data
     * @ignore
     */
    function readObjectGroup(map, data, z) {
        return (new me.TMXObjectGroup(data.name, data, map.orientation, map.tilesets, z));
    }

    /**
     * a TMX Tile Map Object
     * Tiled QT +0.7.x format
     * @class
     * @extends me.Object
     * @memberOf me
     * @constructor
     * @param {String} levelId name of TMX map
     * @param {Object} data TMX map in JSON format
     * @example
     * // create a new level object based on the TMX JSON object
     * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
     * // add the level to the game world container
     * level.addTo(me.game.world, true);
     */
    me.TMXTileMap = me.Object.extend({
        // constructor
        init: function (levelId, data) {

            /**
             * name of the tilemap
             * @public
             * @type String
             * @name me.TMXTileMap#name
             */
            this.name = levelId;

            /**
             * the level data (JSON)
             * @ignore
             */
            this.data = data;

            /**
             * width of the tilemap in tiles
             * @public
             * @type Int
             * @name me.TMXTileMap#cols
             */
            this.cols = +data.width;
            /**
             * height of the tilemap in tiles
             * @public
             * @type Int
             * @name me.TMXTileMap#rows
             */
            this.rows = +data.height;

            /**
             * Tile width
             * @public
             * @type Int
             * @name me.TMXTileMap#tilewidth
             */
            this.tilewidth = +data.tilewidth;

            /**
             * Tile height
             * @public
             * @type Int
             * @name me.TMXTileMap#tileheight
             */
            this.tileheight = +data.tileheight;

            // tilesets for this map
            this.tilesets = null;
            // layers
            this.layers = [];
            // group objects
            this.objectGroups = [];

            // tilemap version
            this.version = data.version;

            // map type (orthogonal or isometric)
            this.orientation = data.orientation;
            if (this.orientation === "isometric") {
                this.width = (this.cols + this.rows) * (this.tilewidth / 2);
                this.height = (this.cols + this.rows) * (this.tileheight / 2);
            } else {
                this.width = this.cols * this.tilewidth;
                this.height = this.rows * this.tileheight;
            }

            // objects minimum z order
            this.z = 0;

            // object id
            this.nextobjectid = +data.nextobjectid || undefined;

            // hex/iso properties
            this.hexsidelength = +data.hexsidelength || undefined;
            this.staggeraxis = data.staggeraxis;
            this.staggerindex = data.staggerindex;

            // background color
            this.backgroundcolor = data.backgroundcolor;

            // set additional map properties (if any)
            me.TMXUtils.applyTMXProperties(this, data);

            // initialize a default TMX renderer
            if ((me.game.tmxRenderer === null) || !me.game.tmxRenderer.canRender(this)) {
                me.game.tmxRenderer = getNewDefaultRenderer(this);
            }

            // internal flag
            this.initialized = false;

        },

        /**
         * parse the map
         * @ignore
         */
        readMapObjects: function (data) {

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

            // to automatically increment z index
            var zOrder = this.z;
            var self = this;

            // Tileset information
            if (!this.tilesets) {
                // make sure we have a TilesetGroup Object
                this.tilesets = new me.TMXTilesetGroup();
            }

            // parse all tileset objects
            if (typeof (data.tilesets) !== "undefined") {
                var tilesets = data.tilesets;
                tilesets.forEach(function (tileset) {
                    // add the new tileset
                    self.tilesets.add(readTileset(tileset));
                });
            }

            // check if a user-defined background color is defined
            if (this.backgroundcolor) {
                this.layers.push(
                    new me.ColorLayer(
                        "background_color",
                        this.backgroundcolor,
                        zOrder++
                    )
                );
            }

            // check if a background image is defined
            if (this.background_image) {
                // add a new image layer
                this.layers.push(new me.ImageLayer(
                    0, 0, {
                        name : "background_image",
                        image : this.background_image,
                        z : zOrder++
                    }
                ));
            }

            data.layers.forEach(function (layer) {
                switch (layer.type) {
                    case "imagelayer":
                        self.layers.push(readImageLayer(self, layer, zOrder++));
                        break;

                    case "tilelayer":
                        self.layers.push(readLayer(self, layer, zOrder++));
                        break;

                    // get the object groups information
                    case "objectgroup":
                        self.objectGroups.push(readObjectGroup(self, layer, zOrder++));
                        break;

                    default:
                        break;
                }
            });
            this.initialized = true;
        },

        /**
         * add all the map layers and objects to the given container
         * @name me.TMXTileMap#addTo
         * @public
         * @function
         * @param {me.Container} target container
         * @param {boolean} flatten if true, flatten all objects into the given container
         * @example
         * // create a new level object based on the TMX JSON object
         * var level = new me.TMXTileMap(levelId, me.loader.getTMX(levelId));
         * // add the level to the game world container
         * level.addTo(me.game.world, true);
         */
        addTo : function (container, flatten) {
            var _sort = container.autoSort;
            var _depth = container.autoDepth;

            // disable auto-sort and auto-depth
            container.autoSort = false;
            container.autoDepth = false;

            // add all layers instances
            this.getLayers().forEach(function (layer) {
                container.addChild(layer);
            });

            // add all Object instances
            this.getObjects(flatten).forEach(function (object) {
                container.addChild(object);
            });

            //  set back auto-sort and auto-depth
            container.autoSort = _sort;
            container.autoDepth = _depth;

            // force a sort
            container.sort(true);
        },

        /**
         * return an Array of instantiated objects, based on the map object definition
         * @name me.TMXTileMap#getObjects
         * @public
         * @function
         * @param {boolean} flatten if true, flatten all objects into the returned array, <br>
         * ignoring all defined groups (no sub containers will be created)
         * @return {me.Renderable[]} Array of Objects
         */
        getObjects : function (flatten) {
            var objects = [];
            var isCollisionGroup = false;
            var targetContainer;

            // parse the map for objects
            this.readMapObjects(this.data);

            for (var g = 0; g < this.objectGroups.length; g++) {
                var group = this.objectGroups[g];

                // check if this is the collision shape group
                isCollisionGroup = group.name.toLowerCase().includes(COLLISION_GROUP);

                if (flatten === false) {
                    // create a new container
                    targetContainer = new me.Container(0, 0, this.width, this.height);

                    // set additional properties
                    targetContainer.name = group.name;
                    targetContainer.pos.z = group.z;
                    targetContainer.setOpacity(group.opacity);

                    // disable auto-sort and auto-depth
                    targetContainer.autoSort = false;
                    targetContainer.autoDepth = false;
                }

                // iterate through the group and add all object into their
                // corresponding target Container
                for (var o = 0; o < group.objects.length; o++) {
                    // TMX object settings
                    var settings = group.objects[o];

                    var obj = me.pool.pull(
                        settings.name || "me.Entity",
                        settings.x, settings.y,
                        settings
                    );
                    // skip if the pull function does not return a corresponding object
                    if (typeof obj !== "object") {
                        continue;
                    }

                    // check if a me.Tile object is embedded
                    if (typeof (settings.tile) === "object" && !obj.renderable) {
                        obj.renderable = settings.tile.getRenderable(settings);
                    }

                    if (isCollisionGroup && !settings.name) {
                        // configure the body accordingly
                        obj.body.collisionType = me.collision.types.WORLD_SHAPE;
                    }

                    // set the obj z order correspondingly to its parent container/group
                    obj.pos.z = group.z;

                    //apply group opacity value to the child objects if group are merged
                    if (flatten === true) {
                        if (obj.isRenderable === true) {
                            obj.setOpacity(obj.getOpacity() * group.opacity);
                            // and to child renderables if any
                            if (obj.renderable instanceof me.Renderable) {
                                obj.renderable.setOpacity(obj.renderable.getOpacity() * group.opacity);
                            }
                        }
                        // directly add the obj into the objects array
                        objects.push(obj);
                    } else /* false*/ {
                        // add it to the new container
                        targetContainer.addChild(obj);
                    }

                }

                // if we created a new container
                if ((flatten === false) && (targetContainer.children.length > 0)) {

                    // re-enable auto-sort and auto-depth
                    targetContainer.autoSort = true;
                    targetContainer.autoDepth = true;

                    // add our container to the world
                    objects.push(targetContainer);
                }
            }
            return objects;
        },

        /**
         * return all the existing layers
         * @name me.TMXTileMap#getLayers
         * @public
         * @function
         * @return {me.TMXLayer[]} Array of Layers
         */
        getLayers : function () {
            // parse the map for objects
            this.readMapObjects(this.data);
            return this.layers;
        },

        /**
         * destroy function, clean all allocated objects
         * @name me.TMXTileMap#destroy
         * @public
         * @function
         */
        destroy : function () {
            this.tilesets = undefined;
            this.layers = [];
            this.objectGroups = [];
            this.initialized = false;
        }
    });
})();
<|endoftext|>


<commit_before>/*
 *
 * Description:
 * This script creates a directory and sends that as an argument to a spawned process (capture.cpp).
 * Then, it sends a request to capture a frame with file name of current time at a given interval.
 * Lastly, when (capture.cpp) responds with the file name (meaning save completed), it reads the file
 * and then emits the content to the Node.js server in base64 (string) format.
 *
 */
var spawn = require('child_process').spawn, util = require('util'), request = require('request'), EventEmitter = require('events').EventEmitter, fs = require('fs'), path = require('path'), CONFIG = require('./config'), logger = require('./logger').create(CONFIG), orutils = require('./orutils'), moment = require('moment');
var OpenROVCamera = function (options) {
  var camera = new EventEmitter();
  var capture_process;
  // Open mjpg_streamer app as a child process
  var cmd = 'mjpg_streamer';
  // rename to correspond with your C++ compilation
  var default_opts = {
      device: CONFIG.video_device,
      resolution: CONFIG.video_resolution,
      framerate: CONFIG.video_frame_rate,
      port: CONFIG.video_port
    };
  options = orutils.mixin(options, default_opts);
  var _capturing = false;
  camera.IsCapturing = function () {
    return _capturing;
  };
  var args = [
      '-i',
      '/usr/local/lib/input_uvc.so -r ' + options.resolution + ' -f ' + options.framerate,
      '-o',
      '/usr/local/lib/output_http.so -p ' + options.port
    ];
  // End camera process gracefully
  camera.close = function () {
    if (!_capturing)
      return;
    logger.log('closing camera on', options.device);
    _capturing = false;
    logger.log('sending SIGHUP to capture process');
    process.kill(capture_process.pid, 'SIGHUP');
  };
  camera.snapshot = function (callback) {
    if (!_capturing)
      return;
    var filename = CONFIG.preferences.get('photoDirectory') + '/ROV' + moment().format('YYYYMMDDHHmmss') + '.jpg';
    request('http://localhost:' + options.port + '/?action=snapshot').pipe(fs.createWriteStream(filename));
    callback(filename);
  };
  // Actual camera capture starting mjpg-stremer
  camera.capture = function (callback) {
    logger.log('initiating camera on', options.device);
    logger.log('ensure beagle is at 100% cpu for this camera');
    spawn('cpufreq-set', [
      '-g',
      'performance'
    ]);
    // if camera working, should be at options.device (most likely /dev/video0 or similar)
    fs.exists(options.device, function (exists) {
      // no camera?!
      if (!exists)
        return callback(new Error(options.device + ' does not exist'));
      // wooooo!  camera!
      logger.log(options.device, ' found');
      _capturing = true;
      // then remember that we're capturing
      logger.log('spawning capture process...');
      capture_process = spawn(cmd, args);
      camera.emit('started');
      capture_process.stdout.on('data', function (data) {
        logger.log('camera: ' + data);
      });
      capture_process.stderr.on('data', function (data) {
        logger.log('camera: ' + data);  //	camera.emit('error.device',data);
      });
      console.log('camera started');
      capture_process.on('exit', function (code) {
        console.log('child process exited with code ' + code);
        _capturing = false;
        camera.emit('error.device', code);
      });
    });
  };
  return camera;
};
module.exports = OpenROVCamera;
<commit_msg>Apllying patch from PNG to restart camera process in case it died. We try up to 10 times to restart the process before giving up. Fixes #312<commit_after>/*
 *
 * Description:
 * This script creates a directory and sends that as an argument to a spawned process (capture.cpp).
 * Then, it sends a request to capture a frame with file name of current time at a given interval.
 * Lastly, when (capture.cpp) responds with the file name (meaning save completed), it reads the file
 * and then emits the content to the Node.js server in base64 (string) format.
 *
 */
var spawn = require('child_process').spawn, util = require('util'), request = require('request'), EventEmitter = require('events').EventEmitter, fs = require('fs'), path = require('path'), CONFIG = require('./config'), logger = require('./logger').create(CONFIG), orutils = require('./orutils'), moment = require('moment');
var OpenROVCamera = function (options) {
  var camera = new EventEmitter();
  var capture_process;
  // Open mjpg_streamer app as a child process
  var cmd = 'mjpg_streamer';
  // rename to correspond with your C++ compilation
  var default_opts = {
      device: CONFIG.video_device,
      resolution: CONFIG.video_resolution,
      framerate: CONFIG.video_frame_rate,
      port: CONFIG.video_port
    };
  options = orutils.mixin(options, default_opts);
  var _capturing = false;
  camera.IsCapturing = function () {
    return _capturing;
  };
  var args = [
      '-i',
      '/usr/local/lib/input_uvc.so -r ' + options.resolution + ' -f ' + options.framerate,
      '-o',
      '/usr/local/lib/output_http.so -p ' + options.port
    ];
  // End camera process gracefully
  camera.close = function () {
    if (!_capturing)
      return;
    logger.log('closing camera on', options.device);
    _capturing = false;
    logger.log('sending SIGHUP to capture process');
    process.kill(capture_process.pid, 'SIGHUP');
  };
  camera.snapshot = function (callback) {
    if (!_capturing)
      return;
    var filename = CONFIG.preferences.get('photoDirectory') + '/ROV' + moment().format('YYYYMMDDHHmmss') + '.jpg';
    request('http://localhost:' + options.port + '/?action=snapshot').pipe(fs.createWriteStream(filename));
    callback(filename);
  };
  var restartCount = 0;
  // Actual camera capture starting mjpg-stremer
  var capture;
  capture = function (callback) {
    logger.log('initiating camera on', options.device);
    logger.log('ensure beagle is at 100% cpu for this camera');
    spawn('cpufreq-set', [
      '-g',
      'performance'
    ]);
    // if camera working, should be at options.device (most likely /dev/video0 or similar)
    fs.exists(options.device, function (exists) {
      // no camera?!
      if (!exists)
        return callback(new Error(options.device + ' does not exist'));
      // wooooo!  camera!
      logger.log(options.device, ' found');
      _capturing = true;
      // then remember that we're capturing
      logger.log('spawning capture process...');
      capture_process = spawn(cmd, args);
      camera.emit('started');
      capture_process.stdout.on('data', function (data) {
        logger.log('camera: ' + data);
      });
      capture_process.stderr.on('data', function (data) {
        logger.log('camera: ' + data);
      });
      console.log('camera started');
      capture_process.on('exit', function (code) {
        console.log('child process exited with code ' + code);
        _capturing = false;
        camera.emit('error.device', code);
        if ( restartCount < 10 ) {
          console.log('starting new camera process for the ' + restartCount + ' time');
          restartCount = restartCount + 1;
          capture(callback);
        }
        else { console.log('camera process crashed too many times. giving up'); }
      });
    });
  };
  camera.capture = capture;
  return camera;
};
module.exports = OpenROVCamera;
<|endoftext|>


<commit_before>import utils from './utils';
import random from './random';
import ParseError from './error';
import inferType from './infer';
import types from '../types/index';
import optionAPI from '../api/option';

// TODO provide types
function traverse(schema, path, resolve, rootSchema) {
  schema = resolve(schema, null, path);

  if (!schema) {
    return;
  }

  // default values has higher precedence
  if (path[path.length - 1] !== 'properties') {
    // example values have highest precedence
    if (optionAPI('useExamplesValue') && Array.isArray(schema.examples)) {
      // include `default` value as example too
      const fixedExamples = schema.examples
        .concat('default' in schema ? [schema.default] : []);

      return utils.typecast(null, schema, () => random.pick(fixedExamples));
    }

    if (optionAPI('useDefaultValue') && 'default' in schema) {
      if (schema.default !== '' || !optionAPI('replaceEmptyByRandomValue')) {
        return schema.default;
      }
    }

    if ('template' in schema) {
      return utils.template(schema.template, rootSchema);
    }

    if ('const' in schema) {
      return schema.const;
    }
  }

  if (schema.not && typeof schema.not === 'object') {
    schema = utils.notValue(schema.not, utils.omitProps(schema, ['not']));

    // build new object value from not-schema!
    if (schema.type && schema.type === 'object') {
      const traverseResult = traverse(schema, path.concat(['not']), resolve, rootSchema);
      return utils.clean(traverseResult, schema, false);
    }
  }

  // thunks can return sub-schemas
  if (typeof schema.thunk === 'function') {
    // result is already cleaned in thunk
    return traverse(schema.thunk(rootSchema), path, resolve);
  }

  if (typeof schema.generate === 'function') {
    const retval = utils.typecast(null, schema, () => schema.generate(rootSchema, path));
    const type = typeof retval;

    if (type === schema.type || (type === 'number' && schema.type === 'integer')) {
      return retval;
    }
  }

  if (typeof schema.pattern === 'string') {
    return utils.typecast('string', schema, () => random.randexp(schema.pattern));
  }

  if (Array.isArray(schema.enum)) {
    return utils.typecast(null, schema, () => random.pick(schema.enum));
  }

  // short-circuit as we don't plan generate more values!
  if (schema.jsonPath) {
    return schema;
  }

  // TODO remove the ugly overcome
  let type = schema.type;

  if (Array.isArray(type)) {
    type = random.pick(type);
  } else if (typeof type === 'undefined') {
    // Attempt to infer the type
    type = inferType(schema, path) || type;

    if (type) {
      schema.type = type;
    }
  }

  if (typeof type === 'string') {
    if (!types[type]) {
      if (optionAPI('failOnInvalidTypes')) {
        throw new ParseError(`unknown primitive ${utils.short(type)}`, path.concat(['type']));
      } else {
        const value = optionAPI('defaultInvalidTypeProduct');

        if (typeof value === 'string' && types[value]) {
          return types[value](schema, path, resolve, traverse);
        }

        return value;
      }
    } else {
      try {
        return types[type](schema, path, resolve, traverse);
      } catch (e) {
        if (typeof e.path === 'undefined') {
          throw new ParseError(e.stack, path);
        }
        throw e;
      }
    }
  }

  let copy = {};

  if (Array.isArray(schema)) {
    copy = [];
  }

  Object.keys(schema).forEach(prop => {
    if (typeof schema[prop] === 'object' && prop !== 'definitions') {
      const traverseResult = traverse(schema[prop], path.concat([prop]), resolve, copy);
      copy[prop] = utils.clean(traverseResult, schema[prop], false);
    } else {
      copy[prop] = schema[prop];
    }
  });

  return copy;
}

export default traverse;
<commit_msg>fix: add type checks for arrays for generator-returned values (#628)<commit_after>import utils from './utils';
import random from './random';
import ParseError from './error';
import inferType from './infer';
import types from '../types/index';
import optionAPI from '../api/option';

// TODO provide types
function traverse(schema, path, resolve, rootSchema) {
  schema = resolve(schema, null, path);

  if (!schema) {
    return;
  }

  // default values has higher precedence
  if (path[path.length - 1] !== 'properties') {
    // example values have highest precedence
    if (optionAPI('useExamplesValue') && Array.isArray(schema.examples)) {
      // include `default` value as example too
      const fixedExamples = schema.examples
        .concat('default' in schema ? [schema.default] : []);

      return utils.typecast(null, schema, () => random.pick(fixedExamples));
    }

    if (optionAPI('useDefaultValue') && 'default' in schema) {
      if (schema.default !== '' || !optionAPI('replaceEmptyByRandomValue')) {
        return schema.default;
      }
    }

    if ('template' in schema) {
      return utils.template(schema.template, rootSchema);
    }

    if ('const' in schema) {
      return schema.const;
    }
  }

  if (schema.not && typeof schema.not === 'object') {
    schema = utils.notValue(schema.not, utils.omitProps(schema, ['not']));

    // build new object value from not-schema!
    if (schema.type && schema.type === 'object') {
      const traverseResult = traverse(schema, path.concat(['not']), resolve, rootSchema);
      return utils.clean(traverseResult, schema, false);
    }
  }

  // thunks can return sub-schemas
  if (typeof schema.thunk === 'function') {
    // result is already cleaned in thunk
    return traverse(schema.thunk(rootSchema), path, resolve);
  }

  if (typeof schema.generate === 'function') {
    const retval = utils.typecast(null, schema, () => schema.generate(rootSchema, path));
    const type = typeof retval;

    if (type === schema.type || (type === 'number' && schema.type === 'integer') || (Array.isArray(retval) && schema.type === 'array')) {
      return retval;
    }
  }

  if (typeof schema.pattern === 'string') {
    return utils.typecast('string', schema, () => random.randexp(schema.pattern));
  }

  if (Array.isArray(schema.enum)) {
    return utils.typecast(null, schema, () => random.pick(schema.enum));
  }

  // short-circuit as we don't plan generate more values!
  if (schema.jsonPath) {
    return schema;
  }

  // TODO remove the ugly overcome
  let type = schema.type;

  if (Array.isArray(type)) {
    type = random.pick(type);
  } else if (typeof type === 'undefined') {
    // Attempt to infer the type
    type = inferType(schema, path) || type;

    if (type) {
      schema.type = type;
    }
  }

  if (typeof type === 'string') {
    if (!types[type]) {
      if (optionAPI('failOnInvalidTypes')) {
        throw new ParseError(`unknown primitive ${utils.short(type)}`, path.concat(['type']));
      } else {
        const value = optionAPI('defaultInvalidTypeProduct');

        if (typeof value === 'string' && types[value]) {
          return types[value](schema, path, resolve, traverse);
        }

        return value;
      }
    } else {
      try {
        return types[type](schema, path, resolve, traverse);
      } catch (e) {
        if (typeof e.path === 'undefined') {
          throw new ParseError(e.stack, path);
        }
        throw e;
      }
    }
  }

  let copy = {};

  if (Array.isArray(schema)) {
    copy = [];
  }

  Object.keys(schema).forEach(prop => {
    if (typeof schema[prop] === 'object' && prop !== 'definitions') {
      const traverseResult = traverse(schema[prop], path.concat([prop]), resolve, copy);
      copy[prop] = utils.clean(traverseResult, schema[prop], false);
    } else {
      copy[prop] = schema[prop];
    }
  });

  return copy;
}

export default traverse;
<|endoftext|>