<commit_before>$(function () {
    $('.blockMap .container').removeClass('container').addClass('containerClass');
    blockDrop.bindUi();
});

function BlockDrop() {
    this.rows = 0;
    this.binded = false;
}

BlockDrop.prototype.bindUi = function () {
    if (this.binded) {
        $(".blockPreset .block,.blockMap .block").draggable("destroy");
        $(".blockPreset .block,.blockMap .block").droppable("destroy");
        $(".blockPreset .block,.blockMap .block").sortable("destroy");
    }

    $(".blockMap .blockCol, .blockPreset").droppable({
        activeClass: "ui-state-default",
        hoverClass: "ui-state-hover",
        accept: ":not(.ui-sortable-helper)",
        drop: function (event, ui) {
            $(this).find(".placeholder").remove();
            $("

").text(ui.draggable.text()).appendTo(this).draggable({
                appendTo: "body",
                revert: true,
            });
            if (ui.draggable.closest('.rows').length > 0) {
                ui.draggable.remove();
            }
        }
    }).sortable({
        items: "div:not(.placeholder)",
        placeholder: "ui-state-highlight",
        sort: function () {
            $(this).removeClass("ui-state-default");
        }
    });
    $(".blockPreset .block,.blockMap .block").draggable({
        appendTo: "body",
        revert: true,
        stack: ".blockPreset .block,.blockMap .block",
        opacity: 0.7,
        connectToSortable: '.blockMap .blockCol, .blockPreset',

    });
    this.binded = true;
}
BlockDrop.prototype.addRow = function (selector) {
    var html = '
';
    html += '
';
    html += 'Число столбцов

';
    html += '
';
    for (var i = 1; i <= 12; i++) {
        html += '\
                    <input type="radio" name="options" autocomplete="off"> ' + i + '\
                ';
    }
    html += '
';
    html += '
';
    html += '
';
    html += '
12
';
    html += '
';
    html += '
<button class = "btn btn-success" onclick = "blockDrop.acceptAddRow(this);" data-dismiss="modal">Добавить строку</button>
';
    html += '
';
    var modal = ui.modal.show('Выбор разметки', html);
}
BlockDrop.prototype.changeCols = function (btn, count) {
    var rowselector = $(btn).closest('.rowsSelector');
    rowselector.find('.colsSelect').html('');
    var parts = 0;
    var part = parseInt(12 / count);
    for (var i = 1; i <= count; i++) {
        parts += part;
        if (parts > 12) {
            part = parts - 12;
        }
        else if (i == count && parts < 12) {
            part = part + 12 - parts;
        }
        rowselector.find('.colsSelect').append('
' + part + '
');
    }
}

BlockDrop.prototype.acceptAddRow = function (btn) {
    var rowselector = $(btn).closest('.rowsSelector');
    var row = rowselector.find('.colsSelect').clone();
    $.each(row.find('.blockCol'), function () {
        $(this).html('');
    })
    $('.blockMap .rows').append(row);
    blockDrop.bindUi();
}

BlockDrop.prototype.submitMap = function (btn) {
    var form = $(btn).closest('form');
    var map = form.find('.rows');
    var reClasses = ['ui-draggable', 'ui-draggable-handle', 'ui-sortable-handle', 'ui-droppable', 'ui-sortable'];
    for (key in reClasses) {
        map.find('.' + reClasses[key]).removeClass(reClasses[key]);
    }
    map.find('.containerClass').removeClass('containerClass').addClass('container');
    $.each(map.find('.block'), function () {
        $(this).html($(this).data('code'));
        $(this).removeAttr('data-code');
    })
    form.find('[name="map"]').val(map.html());
    //console.log(map.html())
    //return false;
    form.submit();
}
BlockDrop.prototype.initActual = function (selector) {
    $.each($(selector).find('.block'), function () {
        var code = $(this).text();
        $(this).text($('[data-code="' + code + '"]').text());
        $(this).data('code', code);
    });
}
var blockDrop = new BlockDrop();
<commit_msg>fix map saver<commit_after>$(function () {
    $('.blockMap .container').removeClass('container').addClass('containerClass');
    blockDrop.bindUi();
});

function BlockDrop() {
    this.rows = 0;
    this.binded = false;
}

BlockDrop.prototype.bindUi = function () {
    if (this.binded) {
        $(".blockPreset .block,.blockMap .block").draggable("destroy");
        $(".blockPreset .block,.blockMap .block").droppable("destroy");
        $(".blockPreset .block,.blockMap .block").sortable("destroy");
    }

    $(".blockMap .blockCol, .blockPreset").droppable({
        activeClass: "ui-state-default",
        hoverClass: "ui-state-hover",
        accept: ":not(.ui-sortable-helper)",
        drop: function (event, ui) {
            $(this).find(".placeholder").remove();
            $("

").text(ui.draggable.text()).appendTo(this).draggable({
                appendTo: "body",
                revert: true,
            });
            if (ui.draggable.closest('.rows').length > 0) {
                ui.draggable.remove();
            }
        }
    }).sortable({
        items: "div:not(.placeholder)",
        placeholder: "ui-state-highlight",
        sort: function () {
            $(this).removeClass("ui-state-default");
        }
    });
    $(".blockPreset .block,.blockMap .block").draggable({
        appendTo: "body",
        revert: true,
        stack: ".blockPreset .block,.blockMap .block",
        opacity: 0.7,
        connectToSortable: '.blockMap .blockCol, .blockPreset',
    });
    this.binded = true;
}
BlockDrop.prototype.addRow = function (selector) {
    var html = '
';
    html += '
';
    html += 'Число столбцов

';
    html += '
';
    for (var i = 1; i <= 12; i++) {
        html += '\
                    <input type="radio" name="options" autocomplete="off"> ' + i + '\
                ';
    }
    html += '
';
    html += '
';
    html += '
';
    html += '
12
';
    html += '
';
    html += '
<button class = "btn btn-success" onclick = "blockDrop.acceptAddRow(this);" data-dismiss="modal">Добавить строку</button>
';
    html += '
';
    var modal = ui.modal.show('Выбор разметки', html);
}
BlockDrop.prototype.changeCols = function (btn, count) {
    var rowselector = $(btn).closest('.rowsSelector');
    rowselector.find('.colsSelect').html('');
    var parts = 0;
    var part = parseInt(12 / count);
    for (var i = 1; i <= count; i++) {
        parts += part;
        if (parts > 12) {
            part = parts - 12;
        }
        else if (i == count && parts < 12) {
            part = part + 12 - parts;
        }
        rowselector.find('.colsSelect').append('
' + part + '
');
    }
}

BlockDrop.prototype.acceptAddRow = function (btn) {
    var rowselector = $(btn).closest('.rowsSelector');
    var row = rowselector.find('.colsSelect').clone();
    $.each(row.find('.blockCol'), function () {
        $(this).html('');
    })
    $('.blockMap .rows').append(row);
    blockDrop.bindUi();
}

BlockDrop.prototype.submitMap = function (btn) {
    var form = $(btn).closest('form');
    var map = form.find('.rows').removeClass('ui-state-hover');
    var reClasses = ['ui-draggable', 'ui-draggable-handle', 'ui-sortable-handle', 'ui-droppable', 'ui-sortable', 'ui-state-hover'];
    for (key in reClasses) {
        map.find('.' + reClasses[key]).removeClass(reClasses[key]);
    }
    map.find('.containerClass').removeClass('containerClass').addClass('container');
    $.each(map.find('.block'), function () {
        $(this).html($(this).data('code'));
        $(this).removeAttr('data-code');
        $(this).removeAttr('style');
    })
    form.find('[name="map"]').val(map.html());
    //console.log(map.html())
    //return false;
    setTimeout(function () {
        form.submit();
    }, 500);

}
BlockDrop.prototype.initActual = function (selector) {
    $.each($(selector).find('.block'), function () {
        var code = $(this).text();
        $(this).text($('[data-code="' + code + '"]').text());
        $(this).data('code', code);
    });
}
var blockDrop = new BlockDrop();
<|endoftext|>


<commit_before>/**
  @module ember-data
*/

import JSONSerializer from 'ember-data/serializers/json-serializer';
import normalizeModelName from 'ember-data/system/normalize-model-name';
import { pluralize, singularize } from 'ember-inflector/lib/system/string';

var dasherize = Ember.String.dasherize;

/**
  Ember Data 2.0 Serializer:

  In Ember Data a Serializer is used to serialize and deserialize
  records when they are transferred in and out of an external source.
  This process involves normalizing property names, transforming
  attribute values and serializing relationships.

  `JSONAPISerializer` supports the http://jsonapi.org/ spec and is the
  serializer recommended by Ember Data.

  This serializer normalizes a JSON API payload that looks like:

  ```js

    // models/player.js
    import DS from "ember-data";

    export default DS.Model.extend({
      name: DS.attr(),
      skill: DS.attr(),
      gamesPlayed: DS.attr(),
      club: DS.belongsTo('club')
    });

    // models/club.js
    import DS from "ember-data";

    export default DS.Model.extend({
      name: DS.attr(),
      location: DS.attr(),
      players: DS.hasMany('player')
    });
  ```

  ```js

    {
      "data": [
        {
          "attributes": {
            "name": "Benfica",
            "location": "Portugal"
          },
          "id": "1",
          "relationships": {
            "players": {
              "data": [
                {
                  "id": "3",
                  "type": "players"
                }
              ]
            }
          },
          "type": "clubs"
        }
      ],
      "included": [
        {
          "attributes": {
            "name": "Eusebio Silva Ferreira",
            "skill": "Rocket shot",
            "games-played": 431
          },
          "id": "3",
          "relationships": {
            "club": {
              "data": {
                "id": "1",
                "type": "clubs"
              }
            }
          },
          "type": "players"
        }
      ]
    }
  ```

  to the format that the Ember Data store expects.

  @class JSONAPISerializer
  @namespace DS
  @extends DS.JSONSerializer
*/
const JSONAPISerializer = JSONSerializer.extend({

  /**
    @method _normalizeDocumentHelper
    @param {Object} documentHash
    @return {Object}
    @private
  */
  _normalizeDocumentHelper: function(documentHash) {

    if (Ember.typeOf(documentHash.data) === 'object') {
      documentHash.data = this._normalizeResourceHelper(documentHash.data);
    } else if (Ember.typeOf(documentHash.data) === 'array') {
      documentHash.data = documentHash.data.map(this._normalizeResourceHelper, this);
    }

    if (Ember.typeOf(documentHash.included) === 'array') {
      documentHash.included = documentHash.included.map(this._normalizeResourceHelper, this);
    }

    return documentHash;
  },

  /**
    @method _normalizeRelationshipDataHelper
    @param {Object} relationshipDataHash
    @return {Object}
    @private
  */
  _normalizeRelationshipDataHelper: function(relationshipDataHash) {
    let type = this.modelNameFromPayloadKey(relationshipDataHash.type);
    relationshipDataHash.type = type;
    return relationshipDataHash;
  },

  /**
    @method _normalizeResourceHelper
    @param {Object} resourceHash
    @return {Object}
    @private
  */
  _normalizeResourceHelper: function(resourceHash) {
    let modelName = this.modelNameFromPayloadKey(resourceHash.type);

    if (!this.store._hasModelFor(modelName)) {
      Ember.warn(this.warnMessageNoModelForType(modelName, resourceHash.type), false, {
        id: 'ds.serializer.model-for-type-missing'
      });
      return null;
    }

    let modelClass = this.store.modelFor(modelName);
    let serializer = this.store.serializerFor(modelName);
    let { data } = serializer.normalize(modelClass, resourceHash);
    return data;
  },

  /**
    @method pushPayload
    @param {DS.Store} store
    @param {Object} payload
  */
  pushPayload: function(store, payload) {
    let normalizedPayload = this._normalizeDocumentHelper(payload);
    store.push(normalizedPayload);
  },

  /**
    @method _normalizeResponse
    @param {DS.Store} store
    @param {DS.Model} primaryModelClass
    @param {Object} payload
    @param {String|Number} id
    @param {String} requestType
    @param {Boolean} isSingle
    @return {Object} JSON-API Document
    @private
  */
  _normalizeResponse: function(store, primaryModelClass, payload, id, requestType, isSingle) {
    let normalizedPayload = this._normalizeDocumentHelper(payload);
    return normalizedPayload;
  },

  /**
    @method extractAttributes
    @param {DS.Model} modelClass
    @param {Object} resourceHash
    @return {Object}
  */
  extractAttributes: function(modelClass, resourceHash) {
    var attributes = {};

    if (resourceHash.attributes) {
      modelClass.eachAttribute((key) => {
        let attributeKey = this.keyForAttribute(key, 'deserialize');
        if (resourceHash.attributes.hasOwnProperty(attributeKey)) {
          attributes[key] = resourceHash.attributes[attributeKey];
        }
      });
    }

    return attributes;
  },

  /**
    @method extractRelationship
    @param {Object} relationshipHash
    @return {Object}
  */
  extractRelationship: function(relationshipHash) {

    if (Ember.typeOf(relationshipHash.data) === 'object') {
      relationshipHash.data = this._normalizeRelationshipDataHelper(relationshipHash.data);
    }

    if (Ember.typeOf(relationshipHash.data) === 'array') {
      relationshipHash.data = relationshipHash.data.map(this._normalizeRelationshipDataHelper, this);
    }

    return relationshipHash;
  },

  /**
    @method extractRelationships
    @param {Object} modelClass
    @param {Object} resourceHash
    @return {Object}
  */
  extractRelationships: function(modelClass, resourceHash) {
    let relationships = {};

    if (resourceHash.relationships) {
      modelClass.eachRelationship((key, relationshipMeta) => {
        let relationshipKey = this.keyForRelationship(key, relationshipMeta.kind, 'deserialize');
        if (resourceHash.relationships.hasOwnProperty(relationshipKey)) {

          let relationshipHash = resourceHash.relationships[relationshipKey];
          relationships[key] = this.extractRelationship(relationshipHash);

        }
      });
    }

    return relationships;
  },

  /**
    @method _extractType
    @param {DS.Model} modelClass
    @param {Object} resourceHash
    @return {String}
    @private
  */
  _extractType: function(modelClass, resourceHash) {
    return this.modelNameFromPayloadKey(resourceHash.type);
  },

  /**
    @method modelNameFromPayloadKey
    @param {String} key
    @return {String} the model's modelName
  */
  modelNameFromPayloadKey: function(key) {
    return singularize(normalizeModelName(key));
  },

  /**
    @method payloadKeyFromModelName
    @param {String} modelName
    @return {String}
  */
  payloadKeyFromModelName: function(modelName) {
    return pluralize(modelName);
  },

  /**
    @method normalize
    @param {DS.Model} modelClass
    @param {Object} resourceHash
    @return {String}
  */
  normalize: function(modelClass, resourceHash) {
    this.normalizeUsingDeclaredMapping(modelClass, resourceHash);

    let data = {
      id:            this.extractId(modelClass, resourceHash),
      type:          this._extractType(modelClass, resourceHash),
      attributes:    this.extractAttributes(modelClass, resourceHash),
      relationships: this.extractRelationships(modelClass, resourceHash)
    };

    this.applyTransforms(modelClass, data.attributes);

    return { data };
  },

  /**
   `keyForAttribute` can be used to define rules for how to convert an
   attribute name in your model to a key in your JSON.
   By default `JSONAPISerializer` follows the format used on the examples of
   http://jsonapi.org/format and uses dashes as the word separator in the JSON
   attribute keys.

   This behaviour can be easily customized by extending this method.

   Example

   ```app/serializers/application.js
   import DS from 'ember-data';

   export default DS.JSONAPISerializer.extend({
     keyForAttribute: function(attr, method) {
       return Ember.String.dasherize(attr).toUpperCase();
     }
   });
   ```

   @method keyForAttribute
   @param {String} key
   @param {String} method
   @return {String} normalized key
  */
  keyForAttribute: function(key, method) {
    return dasherize(key);
  },

  /**
   `keyForRelationship` can be used to define a custom key when
   serializing and deserializing relationship properties.
   By default `JSONAPISerializer` follows the format used on the examples of
   http://jsonapi.org/format and uses dashes as word separators in
   relationship properties.

   This behaviour can be easily customized by extending this method.

   Example

    ```app/serializers/post.js
    import DS from 'ember-data';

    export default DS.JSONAPISerializer.extend({
      keyForRelationship: function(key, relationship, method) {
        return Ember.String.underscore(key);
      }
    });
    ```
   @method keyForRelationship
   @param {String} key
   @param {String} typeClass
   @param {String} method
   @return {String} normalized key
  */
  keyForRelationship: function(key, typeClass, method) {
    return dasherize(key);
  },

  /**
    @method serialize
    @param {DS.Snapshot} snapshot
    @param {Object} options
    @return {Object} json
  */
  serialize: function(snapshot, options) {
    let data = this._super(...arguments);
    data.type = this.payloadKeyFromModelName(snapshot.modelName);
    return { data };
  },

  /**
   @method serializeAttribute
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {String} key
   @param {Object} attribute
  */
  serializeAttribute: function(snapshot, json, key, attribute) {
    var type = attribute.type;

    if (this._canSerialize(key)) {
      json.attributes = json.attributes || {};

      var value = snapshot.attr(key);
      if (type) {
        var transform = this.transformFor(type);
        value = transform.serialize(value);
      }

      var payloadKey =  this._getMappedKey(key);
      if (payloadKey === key) {
        payloadKey = this.keyForAttribute(key, 'serialize');
      }

      json.attributes[payloadKey] = value;
    }
  },

  /**
   @method serializeBelongsTo
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {Object} relationship
  */
  serializeBelongsTo: function(snapshot, json, relationship) {
    var key = relationship.key;

    if (this._canSerialize(key)) {
      var belongsTo = snapshot.belongsTo(key);
      if (belongsTo !== undefined) {

        json.relationships = json.relationships || {};

        var payloadKey = this._getMappedKey(key);
        if (payloadKey === key) {
          payloadKey = this.keyForRelationship(key, 'belongsTo', 'serialize');
        }

        let data = null;
        if (belongsTo) {
          data = {
            type: this.payloadKeyFromModelName(belongsTo.modelName),
            id: belongsTo.id
          };
        }

        json.relationships[payloadKey] = { data };
      }
    }
  },

  /**
   @method serializeHasMany
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {Object} relationship
  */
  serializeHasMany: function(snapshot, json, relationship) {
    var key = relationship.key;

    if (this._shouldSerializeHasMany(snapshot, key, relationship)) {
      var hasMany = snapshot.hasMany(key);
      if (hasMany !== undefined) {

        json.relationships = json.relationships || {};

        var payloadKey = this._getMappedKey(key);
        if (payloadKey === key && this.keyForRelationship) {
          payloadKey = this.keyForRelationship(key, 'hasMany', 'serialize');
        }

        let data = hasMany.map((item) => {
          return {
            type: this.payloadKeyFromModelName(item.modelName),
            id: item.id
          };
        });

        json.relationships[payloadKey] = { data };
      }
    }
  }
});

Ember.runInDebug(function() {
  JSONAPISerializer.reopen({
    warnMessageNoModelForType: function(modelName, originalType) {
      return 'Encountered a resource object with type "' + originalType + '", but no model was found for model name "' + modelName + '" (resolved model name using ' + this.constructor.toString() + '.modelNameFromPayloadKey("' + originalType + '"))';
    }
  });
});

export default JSONAPISerializer;
<commit_msg>[BUGFIX beta] Fix JSONAPISerializer.normalize docs<commit_after>/**
  @module ember-data
*/

import JSONSerializer from 'ember-data/serializers/json-serializer';
import normalizeModelName from 'ember-data/system/normalize-model-name';
import { pluralize, singularize } from 'ember-inflector/lib/system/string';

var dasherize = Ember.String.dasherize;

/**
  Ember Data 2.0 Serializer:

  In Ember Data a Serializer is used to serialize and deserialize
  records when they are transferred in and out of an external source.
  This process involves normalizing property names, transforming
  attribute values and serializing relationships.

  `JSONAPISerializer` supports the http://jsonapi.org/ spec and is the
  serializer recommended by Ember Data.

  This serializer normalizes a JSON API payload that looks like:

  ```js

    // models/player.js
    import DS from "ember-data";

    export default DS.Model.extend({
      name: DS.attr(),
      skill: DS.attr(),
      gamesPlayed: DS.attr(),
      club: DS.belongsTo('club')
    });

    // models/club.js
    import DS from "ember-data";

    export default DS.Model.extend({
      name: DS.attr(),
      location: DS.attr(),
      players: DS.hasMany('player')
    });
  ```

  ```js

    {
      "data": [
        {
          "attributes": {
            "name": "Benfica",
            "location": "Portugal"
          },
          "id": "1",
          "relationships": {
            "players": {
              "data": [
                {
                  "id": "3",
                  "type": "players"
                }
              ]
            }
          },
          "type": "clubs"
        }
      ],
      "included": [
        {
          "attributes": {
            "name": "Eusebio Silva Ferreira",
            "skill": "Rocket shot",
            "games-played": 431
          },
          "id": "3",
          "relationships": {
            "club": {
              "data": {
                "id": "1",
                "type": "clubs"
              }
            }
          },
          "type": "players"
        }
      ]
    }
  ```

  to the format that the Ember Data store expects.

  @class JSONAPISerializer
  @namespace DS
  @extends DS.JSONSerializer
*/
const JSONAPISerializer = JSONSerializer.extend({

  /**
    @method _normalizeDocumentHelper
    @param {Object} documentHash
    @return {Object}
    @private
  */
  _normalizeDocumentHelper: function(documentHash) {

    if (Ember.typeOf(documentHash.data) === 'object') {
      documentHash.data = this._normalizeResourceHelper(documentHash.data);
    } else if (Ember.typeOf(documentHash.data) === 'array') {
      documentHash.data = documentHash.data.map(this._normalizeResourceHelper, this);
    }

    if (Ember.typeOf(documentHash.included) === 'array') {
      documentHash.included = documentHash.included.map(this._normalizeResourceHelper, this);
    }

    return documentHash;
  },

  /**
    @method _normalizeRelationshipDataHelper
    @param {Object} relationshipDataHash
    @return {Object}
    @private
  */
  _normalizeRelationshipDataHelper: function(relationshipDataHash) {
    let type = this.modelNameFromPayloadKey(relationshipDataHash.type);
    relationshipDataHash.type = type;
    return relationshipDataHash;
  },

  /**
    @method _normalizeResourceHelper
    @param {Object} resourceHash
    @return {Object}
    @private
  */
  _normalizeResourceHelper: function(resourceHash) {
    let modelName = this.modelNameFromPayloadKey(resourceHash.type);

    if (!this.store._hasModelFor(modelName)) {
      Ember.warn(this.warnMessageNoModelForType(modelName, resourceHash.type), false, {
        id: 'ds.serializer.model-for-type-missing'
      });
      return null;
    }

    let modelClass = this.store.modelFor(modelName);
    let serializer = this.store.serializerFor(modelName);
    let { data } = serializer.normalize(modelClass, resourceHash);
    return data;
  },

  /**
    @method pushPayload
    @param {DS.Store} store
    @param {Object} payload
  */
  pushPayload: function(store, payload) {
    let normalizedPayload = this._normalizeDocumentHelper(payload);
    store.push(normalizedPayload);
  },

  /**
    @method _normalizeResponse
    @param {DS.Store} store
    @param {DS.Model} primaryModelClass
    @param {Object} payload
    @param {String|Number} id
    @param {String} requestType
    @param {Boolean} isSingle
    @return {Object} JSON-API Document
    @private
  */
  _normalizeResponse: function(store, primaryModelClass, payload, id, requestType, isSingle) {
    let normalizedPayload = this._normalizeDocumentHelper(payload);
    return normalizedPayload;
  },

  /**
    @method extractAttributes
    @param {DS.Model} modelClass
    @param {Object} resourceHash
    @return {Object}
  */
  extractAttributes: function(modelClass, resourceHash) {
    var attributes = {};

    if (resourceHash.attributes) {
      modelClass.eachAttribute((key) => {
        let attributeKey = this.keyForAttribute(key, 'deserialize');
        if (resourceHash.attributes.hasOwnProperty(attributeKey)) {
          attributes[key] = resourceHash.attributes[attributeKey];
        }
      });
    }

    return attributes;
  },

  /**
    @method extractRelationship
    @param {Object} relationshipHash
    @return {Object}
  */
  extractRelationship: function(relationshipHash) {

    if (Ember.typeOf(relationshipHash.data) === 'object') {
      relationshipHash.data = this._normalizeRelationshipDataHelper(relationshipHash.data);
    }

    if (Ember.typeOf(relationshipHash.data) === 'array') {
      relationshipHash.data = relationshipHash.data.map(this._normalizeRelationshipDataHelper, this);
    }

    return relationshipHash;
  },

  /**
    @method extractRelationships
    @param {Object} modelClass
    @param {Object} resourceHash
    @return {Object}
  */
  extractRelationships: function(modelClass, resourceHash) {
    let relationships = {};

    if (resourceHash.relationships) {
      modelClass.eachRelationship((key, relationshipMeta) => {
        let relationshipKey = this.keyForRelationship(key, relationshipMeta.kind, 'deserialize');
        if (resourceHash.relationships.hasOwnProperty(relationshipKey)) {

          let relationshipHash = resourceHash.relationships[relationshipKey];
          relationships[key] = this.extractRelationship(relationshipHash);

        }
      });
    }

    return relationships;
  },

  /**
    @method _extractType
    @param {DS.Model} modelClass
    @param {Object} resourceHash
    @return {String}
    @private
  */
  _extractType: function(modelClass, resourceHash) {
    return this.modelNameFromPayloadKey(resourceHash.type);
  },

  /**
    @method modelNameFromPayloadKey
    @param {String} key
    @return {String} the model's modelName
  */
  modelNameFromPayloadKey: function(key) {
    return singularize(normalizeModelName(key));
  },

  /**
    @method payloadKeyFromModelName
    @param {String} modelName
    @return {String}
  */
  payloadKeyFromModelName: function(modelName) {
    return pluralize(modelName);
  },

  /**
    @method normalize
    @param {DS.Model} modelClass
    @param {Object} resourceHash the resource hash from the adapter
    @return {Object} the normalized resource hash
  */
  normalize: function(modelClass, resourceHash) {
    this.normalizeUsingDeclaredMapping(modelClass, resourceHash);

    let data = {
      id:            this.extractId(modelClass, resourceHash),
      type:          this._extractType(modelClass, resourceHash),
      attributes:    this.extractAttributes(modelClass, resourceHash),
      relationships: this.extractRelationships(modelClass, resourceHash)
    };

    this.applyTransforms(modelClass, data.attributes);

    return { data };
  },

  /**
   `keyForAttribute` can be used to define rules for how to convert an
   attribute name in your model to a key in your JSON.
   By default `JSONAPISerializer` follows the format used on the examples of
   http://jsonapi.org/format and uses dashes as the word separator in the JSON
   attribute keys.

   This behaviour can be easily customized by extending this method.

   Example

   ```app/serializers/application.js
   import DS from 'ember-data';

   export default DS.JSONAPISerializer.extend({
     keyForAttribute: function(attr, method) {
       return Ember.String.dasherize(attr).toUpperCase();
     }
   });
   ```

   @method keyForAttribute
   @param {String} key
   @param {String} method
   @return {String} normalized key
  */
  keyForAttribute: function(key, method) {
    return dasherize(key);
  },

  /**
   `keyForRelationship` can be used to define a custom key when
   serializing and deserializing relationship properties.
   By default `JSONAPISerializer` follows the format used on the examples of
   http://jsonapi.org/format and uses dashes as word separators in
   relationship properties.

   This behaviour can be easily customized by extending this method.

   Example

    ```app/serializers/post.js
    import DS from 'ember-data';

    export default DS.JSONAPISerializer.extend({
      keyForRelationship: function(key, relationship, method) {
        return Ember.String.underscore(key);
      }
    });
    ```
   @method keyForRelationship
   @param {String} key
   @param {String} typeClass
   @param {String} method
   @return {String} normalized key
  */
  keyForRelationship: function(key, typeClass, method) {
    return dasherize(key);
  },

  /**
    @method serialize
    @param {DS.Snapshot} snapshot
    @param {Object} options
    @return {Object} json
  */
  serialize: function(snapshot, options) {
    let data = this._super(...arguments);
    data.type = this.payloadKeyFromModelName(snapshot.modelName);
    return { data };
  },

  /**
   @method serializeAttribute
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {String} key
   @param {Object} attribute
  */
  serializeAttribute: function(snapshot, json, key, attribute) {
    var type = attribute.type;

    if (this._canSerialize(key)) {
      json.attributes = json.attributes || {};

      var value = snapshot.attr(key);
      if (type) {
        var transform = this.transformFor(type);
        value = transform.serialize(value);
      }

      var payloadKey =  this._getMappedKey(key);
      if (payloadKey === key) {
        payloadKey = this.keyForAttribute(key, 'serialize');
      }

      json.attributes[payloadKey] = value;
    }
  },

  /**
   @method serializeBelongsTo
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {Object} relationship
  */
  serializeBelongsTo: function(snapshot, json, relationship) {
    var key = relationship.key;

    if (this._canSerialize(key)) {
      var belongsTo = snapshot.belongsTo(key);
      if (belongsTo !== undefined) {

        json.relationships = json.relationships || {};

        var payloadKey = this._getMappedKey(key);
        if (payloadKey === key) {
          payloadKey = this.keyForRelationship(key, 'belongsTo', 'serialize');
        }

        let data = null;
        if (belongsTo) {
          data = {
            type: this.payloadKeyFromModelName(belongsTo.modelName),
            id: belongsTo.id
          };
        }

        json.relationships[payloadKey] = { data };
      }
    }
  },

  /**
   @method serializeHasMany
   @param {DS.Snapshot} snapshot
   @param {Object} json
   @param {Object} relationship
  */
  serializeHasMany: function(snapshot, json, relationship) {
    var key = relationship.key;

    if (this._shouldSerializeHasMany(snapshot, key, relationship)) {
      var hasMany = snapshot.hasMany(key);
      if (hasMany !== undefined) {

        json.relationships = json.relationships || {};

        var payloadKey = this._getMappedKey(key);
        if (payloadKey === key && this.keyForRelationship) {
          payloadKey = this.keyForRelationship(key, 'hasMany', 'serialize');
        }

        let data = hasMany.map((item) => {
          return {
            type: this.payloadKeyFromModelName(item.modelName),
            id: item.id
          };
        });

        json.relationships[payloadKey] = { data };
      }
    }
  }
});

Ember.runInDebug(function() {
  JSONAPISerializer.reopen({
    warnMessageNoModelForType: function(modelName, originalType) {
      return 'Encountered a resource object with type "' + originalType + '", but no model was found for model name "' + modelName + '" (resolved model name using ' + this.constructor.toString() + '.modelNameFromPayloadKey("' + originalType + '"))';
    }
  });
});

export default JSONAPISerializer;
<|endoftext|>


<commit_before>/*
* isortope v1.3.0
* Simple, animated JavaScript table sorting
*
* https://github.com/KurtPreston/isortope
*/

// Convert cells for comparison
var isortopeNumToString = function(number) {
  // Note: the huge offset is to enable negative numbers to be compared as strings
  number = parseFloat(number) + 450359962737;
  var length = 40; // Create a string of length 40
  var str = number.toString();
  var numDigits = Math.floor(Math.log(number) / Math.LN10);

  for(var i = numDigits; i < length; i++) {
    str = '0' + str;
  }

  return str;
};

var isortopeParseString = function(text) {
  // Remove whitespace
  text = text.replace(/^\s+|\s+$/g, "");

  // Check first word
  var numText = text.split(' ')[0].replace(/[^a-zA-Z0-9\.-]/g, '');

  if (!isNaN(parseFloat(numText))) {
    // Text is a float or integer
    return isortopeNumToString(numText);
  } else {
    // Plain text
    return text.toLowerCase();
  }
};

var isortopeCellFilter = function(element) {
  var text = $(element).text().replace(/^\s+|\s+$/g, '');
  var input = $(element).find('input');
  var returnVal;

  if (text !== '') {
    // Cell has text
    returnVal = isortopeParseString(text);
  } else if (input.length > 0) {
    // If there are inputs
    if (input.val() == 'on') {
      // Check box
      returnVal = input.is(':checked').toString();
    } else {
      returnVal = isortopeParseString(input.val());
    }
  } else {
    // No text or inputs... sort by raw HTML
    returnVal = $(element).html();
  }

  return returnVal;
};

(function($, doc, win) {
  "use strict";

  function Isortope(el, opts) {
    this.$el = $(el);

    this.defaults = {
      autoResort: true,
      autoResortInput: true,
      autoResortContent: true
    };

    this.opts = $.extend(this.defaults, opts, {
      autoResort: this.$el.data('isortope-autoresort'),
      autoResortInput: this.$el.data('isortope-autoresort-input'),
      autoResortContent: this.$el.data('isortope-autoresort-content')
    });

    this.resort = function() {
      this.$el.data('isortope').sortTable();
    };

    this.init();

    // Assign isortope object to HTML element
    this.$el.data('isortope', this);
  }

  $.fn.isortope = function(opts) {
    if(opts == 'resort') {
      return this.each(function() {
        var rows = $(this).find('tbody tr');
        $(this).find('tbody').isotope('updateSortData', rows);
        $(this).data('isortope').resort();
      });
    } else {
      return this.each(function() {
        new Isortope(this, opts);
      });
    }
  };

  Isortope.prototype.init = function() {
    var table = this.$el;
    var tbody = table.find('tbody');
    var rows = table.find('tr');

    // Fix position
    table.css('position', 'relative');
    table.css('height', table.height());

    // Fix column width
    var numCols = table.find('th').length;
    for(var col = 0; col < numCols; col++) {
      var colWidth = table.find('tr:first-child td:nth-child(' + (col + 1) + ')').width();
      table.find('tr td:nth-child(' + (col + 1) + ')').css('width', colWidth);

      // Set th width
      var th = table.find('th:nth-child(' + (col + 1) + ')');
      var thWidth = th.width();
      th.css('width', thWidth);
      th.css('max-width', thWidth);
    }

    // Fix row height
    rows.each(function(index, el) {
      $(el).css('height', $(el).height());
    });

    // Create border style wrapping functions for spacing fix
    // All isotope calls should be wrapped in clearBorders() and restoreBorders()
    var cells = tbody.find('td');
    var borderTopHeight = cells.css('border-top-width');
    var borderBottomHeight = cells.css('border-bottom-width');
    var clearBorders = function () {
      cells.css('border-top-width', 0);
      cells.css('border-bottom-width', 0);
    };
    var restoreBorders = function() {
      cells.css('border-top-width', borderTopHeight);
      cells.css('border-bottom-width', borderBottomHeight);
    };

    // If table has 'separate' cells, change the spacing into margins
    if (table.css('border-collapse') == 'separate') {
      var rows = tbody.find('tr');
      var marginSpacing = parseInt(table.css('border-spacing').split(' ')[0]);
      var borderSpacing = parseInt(borderBottomHeight);
      rows.css('margin-bottom', marginSpacing + borderSpacing + 'px');
    }

    // Define sorters
    var sorters = {};

    for(var col = 0; col < numCols; col++) {
      var colClass = 'col' + col;
      var th = table.find('th:nth-child(' + (col + 1) + ')');
      if (th.attr('data-sort-type') != 'none') {
        var sortFunctionDef = "return isortopeCellFilter(item.find('." + colClass + "'));";
        var sortFunction = new Function('item', sortFunctionDef);
        sorters[colClass] = sortFunction;
        th.attr('data-sort-type', colClass);
        th.css('cursor', 'pointer');
        table.find('tr td:nth-child(' + (col + 1) + ')').addClass(colClass);
        table.find('tr td:nth-child(' + (col + 1) + ')').data('sort-type',colClass);
      }
    }

    // Get yOffset before isotope changes it
    var headerHeight = table.find('thead').offset().top;

    tbody.each(function(index, el) {
      var yOffset = $(el).offset().top - headerHeight;
      $(el).data('offsetHeight', yOffset);
    });

    // Initialize isotope
    clearBorders();

    tbody.each(function(index, el) {
      $(el).isotope({
        itemSelector: 'tr',
        layout: 'fitRows',
        getSortData: sorters
      });
    });

    restoreBorders();

    // Style
    tbody.each(function(index, el) {
      // Restore yOffset
      var yOffset = $(el).data('offsetHeight');
      $(el).find('tr').css('top', yOffset);
    });
    var th = table.find('th');
    th.height(th.height());
    th.css('line-height', 1);

    var removeSortArrow = function() {
      var activeHeader = table.find('th.sortAsc,th.sortDesc');
      activeHeader.find('.sort-arrow').remove();
      activeHeader.removeClass('sortAsc').removeClass('sortDesc');
    };

    // Header click handlers
    table.find('th').click(function() {
      var sort = $(this).attr('data-sort-type');
      if (sort == 'none') {
        return;
      }

      var reverse;

      if ($(this).hasClass('sortAsc')) {
        // Switch to sortDesc
        reverse = true;
        removeSortArrow();
        $(this).html($(this).html() + '\u25BC');
        $(this).addClass('sortDesc');
      } else {
        // Switch to sortAsc
        reverse = false;
        removeSortArrow();
        $(this).html($(this).html() + '\u25B2');
        $(this).addClass('sortAsc');
      }

      sortTable();
    });

    var sortTable = function() {
      var $th = $(table).find('th.sortAsc, th.sortDesc').closest('th');
      var sort = $th.attr('data-sort-type');
      var reverse = $th.hasClass('sortDesc');

      clearBorders();
      tbody.isotope({
        sortBy: sort,
        sortAscending: !reverse,
      });
      restoreBorders();

      table.trigger('sort');
    };
    this.sortTable = sortTable;

    // Update sort data if fields change
    var cellChanged = function(cell) {
      var parentRow = $(cell).closest('tr');
      tbody.isotope('updateSortData', parentRow);

      var column = $(cell).data('sort-type');
      var columnHeader = $('th[data-sort-type='+column+']');

      // Only re-sort if this column is the sort column
      if(columnHeader.hasClass('sortAsc') || columnHeader.hasClass('sortDesc'))
      {
        sortTable();
      }
    };

    if (this.opts.autoResort && this.opts.autoResortInput) {
      // Update sort data if input value changes
      table.find('input').change(function() {
        var cell=$(this).parent('td');
        cellChanged(cell);
      });
    }

    if (this.opts.autoResort && this.opts.autoResortContent) {
      // Update sort data if cell text changes
      const mutationObserver = new MutationObserver((mutationRecords) => {
        mutationRecords.forEach((mutationRecord) => {
          const cell = $(mutationRecord.target).closest('td');
          cellChanged(cell);
        });
      });
      table.find('td').each((i, el) => {
        mutationObserver.observe(el, {
          childList: true,
          characterData: true,
          subtree: true
        });
      });
    }

    table.trigger('initialized');
  };
})(jQuery, document, window);

$(document).ready(function() {
  $('table.isortope').isortope();
});
<commit_msg>Cleaning code (var -> const, removing functions from global scope)<commit_after>/*
* isortope v1.3.0
* Simple, animated JavaScript table sorting
*
* https://github.com/KurtPreston/isortope
*/

(function($, doc, win) {
  "use strict";

  function Isortope(el, opts) {
    //---------- Initialization ---------------
    this.$el = $(el);

    this.defaults = {
      autoResort: true,
      autoResortInput: true,
      autoResortContent: true
    };

    this.opts = $.extend(this.defaults, opts, {
      autoResort: this.$el.data('isortope-autoresort'),
      autoResortInput: this.$el.data('isortope-autoresort-input'),
      autoResortContent: this.$el.data('isortope-autoresort-content')
    });

    this.resort = function() {
      this.$el.data('isortope').sortTable();
    };

    this.init();

    // Assign isortope object to HTML element
    this.$el.data('isortope', this);
  }

  //---------- Helper functions ---------------

  // Convert cells for comparison
  function isortopeNumToString(number) {
    // Note: the huge offset is to enable negative numbers to be compared as strings
    number = parseFloat(number) + 450359962737;
    const length = 40; // Create a string of length 40
    let str = number.toString();
    const numDigits = Math.floor(Math.log(number) / Math.LN10);

    for(let i = numDigits; i < length; i++) {
      str = '0' + str;
    }

    return str;
  };

  function isortopeParseString(text) {
    // Remove whitespace
    text = text.replace(/^\s+|\s+$/g, "");

    // Check first word
    const numText = text.split(' ')[0].replace(/[^a-zA-Z0-9\.-]/g, '');

    if (!isNaN(parseFloat(numText))) {
      // Text is a float or integer
      return isortopeNumToString(numText);
    } else {
      // Plain text
      return text.toLowerCase();
    }
  };

  function isortopeCellFilter(element) {
    const text = $(element).text().replace(/^\s+|\s+$/g, '');
    const input = $(element).find('input');

    if (text !== '') {
      // Cell has text
      return isortopeParseString(text);
    } else if (input.length > 0) {
      // If there are inputs
      if (input.val() == 'on') {
        // Check box
        return input.is(':checked').toString();
      } else {
        return isortopeParseString(input.val());
      }
    } else {
      // No text or inputs... sort by raw HTML
      return $(element).html();
    }
  };

  //---------- Main function ---------------
  $.fn.isortope = function(opts) {
    if(opts == 'resort') {
      return this.each(function() {
        const rows = $(this).find('tbody tr');
        $(this).find('tbody').isotope('updateSortData', rows);
        $(this).data('isortope').resort();
      });
    } else {
      return this.each(function() {
        new Isortope(this, opts);
      });
    }
  };

  Isortope.prototype.init = function() {
    const table = this.$el;
    const tbody = table.find('tbody');
    const rows = table.find('tr');

    // Fix position
    table.css('position', 'relative');
    table.css('height', table.height());

    // Fix column width
    const numCols = table.find('th').length;
    for(let col = 0; col < numCols; col++) {
      const colWidth = table.find('tr:first-child td:nth-child(' + (col + 1) + ')').width();
      table.find('tr td:nth-child(' + (col + 1) + ')').css('width', colWidth);

      // Set th width
      const th = table.find('th:nth-child(' + (col + 1) + ')');
      const thWidth = th.width();
      th.css('width', thWidth);
      th.css('max-width', thWidth);
    }

    // Fix row height
    rows.each(function(index, el) {
      $(el).css('height', $(el).height());
    });

    // Create border style wrapping functions for spacing fix
    // All isotope calls should be wrapped in clearBorders() and restoreBorders()
    const cells = tbody.find('td');
    const borderTopHeight = cells.css('border-top-width');
    const borderBottomHeight = cells.css('border-bottom-width');
    const clearBorders = function () {
      cells.css('border-top-width', 0);
      cells.css('border-bottom-width', 0);
    };
    const restoreBorders = function() {
      cells.css('border-top-width', borderTopHeight);
      cells.css('border-bottom-width', borderBottomHeight);
    };

    // If table has 'separate' cells, change the spacing into margins
    if (table.css('border-collapse') == 'separate') {
      const rows = tbody.find('tr');
      const marginSpacing = parseInt(table.css('border-spacing').split(' ')[0]);
      const borderSpacing = parseInt(borderBottomHeight);
      rows.css('margin-bottom', marginSpacing + borderSpacing + 'px');
    }

    // Define sorters
    const sorters = {};

    for(let col = 0; col < numCols; col++) {
      const colClass = 'col' + col;
      const th = table.find('th:nth-child(' + (col + 1) + ')');
      if (th.attr('data-sort-type') != 'none') {
        // const sortFunctionDef = "return isortopeCellFilter(item.find('." + colClass + "'));";
        // const sortFunction = new Function('item', sortFunctionDef);
        const sortFunction = (item) => {
          const cell = item.find('.' + colClass);
          return isortopeCellFilter(cell);
        }
        sorters[colClass] = sortFunction;
        th.attr('data-sort-type', colClass);
        th.css('cursor', 'pointer');
        table.find('tr td:nth-child(' + (col + 1) + ')').addClass(colClass);
        table.find('tr td:nth-child(' + (col + 1) + ')').data('sort-type',colClass);
      }
    }

    // Get yOffset before isotope changes it
    const headerHeight = table.find('thead').offset().top;

    tbody.each(function(index, el) {
      const yOffset = $(el).offset().top - headerHeight;
      $(el).data('offsetHeight', yOffset);
    });

    // Initialize isotope
    clearBorders();

    tbody.each(function(index, el) {
      $(el).isotope({
        itemSelector: 'tr',
        layout: 'fitRows',
        getSortData: sorters
      });
    });

    restoreBorders();

    // Style
    tbody.each(function(index, el) {
      // Restore yOffset
      const yOffset = $(el).data('offsetHeight');
      $(el).find('tr').css('top', yOffset);
    });
    const th = table.find('th');
    th.height(th.height());
    th.css('line-height', 1);

    const removeSortArrow = function() {
      const activeHeader = table.find('th.sortAsc,th.sortDesc');
      activeHeader.find('.sort-arrow').remove();
      activeHeader.removeClass('sortAsc').removeClass('sortDesc');
    };

    // Header click handlers
    table.find('th').click(function() {
      const sort = $(this).attr('data-sort-type');
      if (sort == 'none') {
        return;
      }

      let reverse;

      if ($(this).hasClass('sortAsc')) {
        // Switch to sortDesc
        reverse = true;
        removeSortArrow();
        $(this).html($(this).html() + '\u25BC');
        $(this).addClass('sortDesc');
      } else {
        // Switch to sortAsc
        reverse = false;
        removeSortArrow();
        $(this).html($(this).html() + '\u25B2');
        $(this).addClass('sortAsc');
      }

      sortTable();
    });

    const sortTable = function() {
      const $th = $(table).find('th.sortAsc, th.sortDesc').closest('th');
      const sort = $th.attr('data-sort-type');
      const reverse = $th.hasClass('sortDesc');

      clearBorders();
      tbody.isotope({
        sortBy: sort,
        sortAscending: !reverse,
      });
      restoreBorders();

      table.trigger('sort');
    };
    this.sortTable = sortTable;

    // Update sort data if fields change
    const cellChanged = function(cell) {
      const parentRow = $(cell).closest('tr');
      tbody.isotope('updateSortData', parentRow);

      const column = $(cell).data('sort-type');
      const columnHeader = $('th[data-sort-type='+column+']');

      // Only re-sort if this column is the sort column
      if(columnHeader.hasClass('sortAsc') || columnHeader.hasClass('sortDesc'))
      {
        sortTable();
      }
    };

    if (this.opts.autoResort && this.opts.autoResortInput) {
      // Update sort data if input value changes
      table.find('input').change(function() {
        const cell=$(this).parent('td');
        cellChanged(cell);
      });
    }

    if (this.opts.autoResort && this.opts.autoResortContent) {
      // Update sort data if cell text changes
      const mutationObserver = new MutationObserver((mutationRecords) => {
        mutationRecords.forEach((mutationRecord) => {
          const cell = $(mutationRecord.target).closest('td');
          cellChanged(cell);
        });
      });
      table.find('td').each((i, el) => {
        mutationObserver.observe(el, {
          childList: true,
          characterData: true,
          subtree: true
        });
      });
    }

    table.trigger('initialized');
  };
})(jQuery, document, window);

$(document).ready(function() {
  $('table.isortope').isortope();
});
<|endoftext|>


<commit_before>'use strict';
var inherits = require('inherits');
var AbstractIterator = require('abstract-leveldown/abstract-iterator');
var IterStream = require('./iter-stream');
var util = require('./encoding');
function goodOptions(opts, name) {
  if (!(name in opts)) {
    return;
  }
  var thing = opts[name];
  if (thing === null) {
    delete opts[name];
    return;
  }
  if (Buffer.isBuffer(thing) || typeof thing === 'string') {
    if (!thing.length) {
      delete opts[name];
      return;
    }

    opts[name] = util.encode(thing);
  }

}
inherits(Iterator, AbstractIterator);
module.exports = Iterator;
var names = [
  'start',
  'end',
  'gt',
  'gte',
  'lt',
  'lte'
];
function Iterator(db, options, cb) {
  AbstractIterator.call(this, db);
  this._db = db.knexDb;
  options = options || {};
  this._order = !options.reverse;
  this._options = options;
  names.forEach(function (i) {
    goodOptions(options, i);
  });
  this._count = 0;
  var self = this;
  if ('limit' in options) {
    this._limit = options.limit;
  } else {
    this._limit = -1;
  }

  if ('keyAsBuffer' in options) {
    this._keyAsBuffer = options.keyAsBuffer;
  } else {
    this._keyAsBuffer = true;
  }
  if ('valueAsBuffer' in options) {
    this._valueAsBuffer = options.valueAsBuffer;
  } else {
    this._valueAsBuffer = true;
  }

  this._sql = this.buildSQL();
  if (this._limit > 0) {
    this._sql.limit(this._limit);
  }
  if (this._limit === 0) {
    this._next = function (cb) {
      process.nextTick(cb);
    };
  } else {
    this._sql = new IterStream(this._sql.stream());
    this.__value = null;
    this.__cb = null;
    this.___cb = cb;
    this._next(function (err, key, value) {
      if (typeof self.__cb === 'function') {
        self.__value = null;
        if (self._ended || (err === void 0 && key === void 0 && value === void 0)) {
          return self.__cb();
        }
        self.__cb(err, key, value);
        self.__cb = null;
      } else {
        self.__value = [err, key, value];
      }
      if (typeof self.___cb === 'function') {
        self.___cb();
        self.___cb = null;
      }
    });
    this.__value = 'in progress';
  }
}
Iterator.prototype._end = function (callback) {
  if (typeof this.___cb === 'function') {
    this.___cb();
    this.___cb = null;
  }
  callback();
};
Iterator.prototype._next = function (callback) {
  var self = this;
  if (self._ended) {
    return callback();
  }
  if (this.__value !== null) {
    if (this.__value === 'in progress') {
      this.__cb = callback;
      return;
    } else {
      return process.nextTick(function () {
        var value = self.__value;
        self.__value = null;
        if (value.every(function (val) {
          return val === void 0;
        })) {
          return callback();
        }
        callback(value[0], value[1], value[2]);
      });
    }
  }
  this._sql.next(function (err, resp) {
    if (err || !resp || !resp.value) {
      return callback();
    }
    var key = util.decode(resp.key, self._keyAsBuffer);
    var value = util.decode(resp.value, self._valueAsBuffer, true);

    if (!self._keyAsBuffer) {
      key = key.toString();
    }
    if (!self._valueAsBuffer) {
      value = value.toString();
    }
    callback(null, key, value);
  });
};

Iterator.prototype.buildSQL = function () {
  var self = this;
  var outersql = this._db.select('key', 'value').from(this.db.tablename);
  var innerSQL = this._db.max('id').from(self.db.tablename).groupBy('key');
  if (this._order)  {
    outersql.orderBy('key');
    if ('start' in this._options) {
      if (this._options.exclusiveStart) {
        if ('start' in this._options) {
          this._options.gt = this._options.start;
        }
      } else {
        if ('start' in this._options) {
          this._options.gte = this._options.start;
        }
      }
    }
    if ('end' in this._options) {
      this._options.lte = this._options.end;
    }
  } else {
    outersql.orderBy('key', 'DESC');
    if ('start' in this._options) {
      if (this._options.exclusiveStart) {
        if ('start' in this._options) {
          this._options.lt = this._options.start;
        }
      } else {
        if ('start' in this._options) {
          this._options.lte = this._options.start;
        }
      }
    }
    if ('end' in this._options) {
      this._options.gte = this._options.end;
    }
  }

  if ('lt' in this._options) {
    innerSQL.where('key','<', this._options.lt);
  }
  if ('lte' in this._options) {
    innerSQL.where('key','<=', this._options.lte);
  }
  if ('gt' in this._options) {
    innerSQL.where('key','>', this._options.gt);
  }
  if ('gte' in this._options) {
    innerSQL.where('key','>=', this._options.gte);
  }
  return outersql.whereIn('id', innerSQL);
};
<commit_msg>remove ._end<commit_after>'use strict';
var inherits = require('inherits');
var AbstractIterator = require('abstract-leveldown/abstract-iterator');
var IterStream = require('./iter-stream');
var util = require('./encoding');
function goodOptions(opts, name) {
  if (!(name in opts)) {
    return;
  }
  var thing = opts[name];
  if (thing === null) {
    delete opts[name];
    return;
  }
  if (Buffer.isBuffer(thing) || typeof thing === 'string') {
    if (!thing.length) {
      delete opts[name];
      return;
    }

    opts[name] = util.encode(thing);
  }

}
inherits(Iterator, AbstractIterator);
module.exports = Iterator;
var names = [
  'start',
  'end',
  'gt',
  'gte',
  'lt',
  'lte'
];
function Iterator(db, options, cb) {
  AbstractIterator.call(this, db);
  this._db = db.knexDb;
  options = options || {};
  this._order = !options.reverse;
  this._options = options;
  names.forEach(function (i) {
    goodOptions(options, i);
  });
  this._count = 0;
  var self = this;
  if ('limit' in options) {
    this._limit = options.limit;
  } else {
    this._limit = -1;
  }

  if ('keyAsBuffer' in options) {
    this._keyAsBuffer = options.keyAsBuffer;
  } else {
    this._keyAsBuffer = true;
  }
  if ('valueAsBuffer' in options) {
    this._valueAsBuffer = options.valueAsBuffer;
  } else {
    this._valueAsBuffer = true;
  }

  this._sql = this.buildSQL();
  if (this._limit > 0) {
    this._sql.limit(this._limit);
  }
  if (this._limit === 0) {
    this._next = function (cb) {
      process.nextTick(cb);
    };
  } else {
    this._sql = new IterStream(this._sql.stream());
    this.__value = null;
    this.__cb = null;
    this.___cb = cb;
    this._next(function (err, key, value) {
      if (typeof self.__cb === 'function') {
        self.__value = null;
        if (self._ended || (err === void 0 && key === void 0 && value === void 0)) {
          return self.__cb();
        }
        self.__cb(err, key, value);
        self.__cb = null;
      } else {
        self.__value = [err, key, value];
      }
      if (typeof self.___cb === 'function') {
        self.___cb();
        self.___cb = null;
      }
    });
    this.__value = 'in progress';
  }
}
// Iterator.prototype._end = function (callback) {
//   if (typeof this.___cb === 'function') {
//     this.___cb();
//     this.___cb = null;
//   }
//   callback();
// };
Iterator.prototype._next = function (callback) {
  var self = this;
  if (self._ended) {
    if (typeof this.___cb === 'function') {
      this.___cb();
      this.___cb = null;
    }
    return callback();
  }
  if (this.__value !== null) {
    if (this.__value === 'in progress') {
      this.__cb = callback;
      return;
    } else {
      return process.nextTick(function () {
        var value = self.__value;
        self.__value = null;
        if (value.every(function (val) {
          return val === void 0;
        })) {
          return callback();
        }
        callback(value[0], value[1], value[2]);
      });
    }
  }
  this._sql.next(function (err, resp) {
    if (err || !resp || !resp.value) {
      return callback();
    }
    var key = util.decode(resp.key, self._keyAsBuffer);
    var value = util.decode(resp.value, self._valueAsBuffer, true);

    if (!self._keyAsBuffer) {
      key = key.toString();
    }
    if (!self._valueAsBuffer) {
      value = value.toString();
    }
    callback(null, key, value);
  });
};

Iterator.prototype.buildSQL = function () {
  var self = this;
  var outersql = this._db.select('key', 'value').from(this.db.tablename);
  var innerSQL = this._db.max('id').from(self.db.tablename).groupBy('key');
  if (this._order)  {
    outersql.orderBy('key');
    if ('start' in this._options) {
      if (this._options.exclusiveStart) {
        if ('start' in this._options) {
          this._options.gt = this._options.start;
        }
      } else {
        if ('start' in this._options) {
          this._options.gte = this._options.start;
        }
      }
    }
    if ('end' in this._options) {
      this._options.lte = this._options.end;
    }
  } else {
    outersql.orderBy('key', 'DESC');
    if ('start' in this._options) {
      if (this._options.exclusiveStart) {
        if ('start' in this._options) {
          this._options.lt = this._options.start;
        }
      } else {
        if ('start' in this._options) {
          this._options.lte = this._options.start;
        }
      }
    }
    if ('end' in this._options) {
      this._options.gte = this._options.end;
    }
  }

  if ('lt' in this._options) {
    innerSQL.where('key','<', this._options.lt);
  }
  if ('lte' in this._options) {
    innerSQL.where('key','<=', this._options.lte);
  }
  if ('gt' in this._options) {
    innerSQL.where('key','>', this._options.gt);
  }
  if ('gte' in this._options) {
    innerSQL.where('key','>=', this._options.gte);
  }
  return outersql.whereIn('id', innerSQL);
};
<|endoftext|>


<commit_before>import { environment } from './environment';
import storage from './storage';

export var toDays = [
  // Daily.
  1,
  // Half weekly.
  3.5,
  // Weekly.
  7,
  // Bi-weekly.
  14,
  // Monthly.
  30
];

/**
 * Schedule a new notification.
 *
 * @param {number} when - time as a moment object or unix timestamp.
 * @param {number} days - how many days until the next notification.
 */
export function create(name, when, days) {
  var minutes = null;

  if (environment === 'chrome') {
    // Convert the repeating days to minutes.
    minutes = days * (24 * 60);

    // When using the same name, Chrome will automatically clear out the previous
    // notification.
    chrome.alarms.create(name, {
      when: Number(when),
      periodInMinutes: minutes
    });
  }
  // In Firefox, the extension library cannot trigger a notification, so
  // instead we let the background script handle it.  Just need to let the
  // extension know when the right time is to trigger.
  else if (environment === 'firefox') {
    self.port.emit('notification.set', when);
  }
}

export function clear(name) {
	if (environment === 'chrome') {
		chrome.alarms.clear(name);
	}
}

export function get1(name) {
	if (environment === 'chrome') {
		return chrome.alarms.get;
	}
}

export function get2(name, callback) {
	chrome.alarms.get(name, function(alarm) { callback(alarm); }
	);
}

export function get(name) {
	return new Promise(function(resolve) {
		chrome.alarms.get(name, resolve);
	});
}

/**
 * Allows to create notification manually, used for thresholdreminders
 */
export function notify(name, type, amount) {

  if (environment === 'chrome') {
    chrome.notifications.create(name, {
      type: 'basic',
      iconUrl: '../img/logo64.png',
      title: 'Tipsy',
      message: 'Time to donate! You have reached your '+ type + ' threshold with an amount of $' + amount + '.'
    }, function unhandledCallback() {});
  } else if (environment === 'firefox') {
    //TODO implement me
    console.error('Threshold notification for Firefox not yet implemented.');
  }
}

function addClickable() {
  chrome.notifications.onButtonClicked.addListener(function(notificationId, buttonIndex) {
    alert("clicked!");
  })
}
/**
 * Listens for Chrome alarms to trigger the next notification.
 */
export function listen(worker) {
  if (environment === 'chrome') {
    storage.get('settings').then(function(settings) {
      var createNotification = function() {
        // Once the alarm triggers, create a notification to dispaly to the
        // user.
        if (settings.moneyIsOwed) {
          chrome.notifications.create("tipsy", {
            type: 'basic',
            iconUrl: '../img/logo64.png',
            title: 'Tipsy',
            message: 'Time to Donate!'
          }, function unhandledCallback() {
            // Reset the next notified in the storage engine.
            var days = toDays[settings.reminderLevel];
            var next = new Date(settings.nextNotified);
            next.setDate(next.getDate() + days);
            settings.nextNotified = Number(next);

            // Create the next alarm.
            create(next, days);

            storage.set('settings', settings);
          });
        addClickable()
        };
      }
      if (Number(settings.nextNotified) < Date.now()) {
        createNotification();
      }

      chrome.alarms.onAlarm.addListener(function() {
        createNotification();
      });
    });
  }

  else if (environment === 'firefox') {
    var notifications = require('sdk/notifications');
    var timers = require('timers');

    // Create a notification timeout upon launching that figures out when, the
    // next reminder should trigger.
    var timeout;

    // Cache this value for easier access.
    var nextNotified = storage.engine.nextNotified;

    // Reset the current notificaiton.
    worker.port.on('notification.set', function(days) {
      timers.clearTimeout(timeout);

      // Cache this value for easier access.
      nextNotified = storage.engine.nextNotified;
      //timeout = setTimeout(showNotification, Date.now() - nextNotified);
    });

    // Reusable function to show the extension and reset.
    //var showNotification = function() {
    //  // Always reset the timeout.
    //  timers.clearTimeout(timeout);

    //  notifications.notify({
    //    title: 'Tipsy',
    //    text: 'Time to donate!'
    //  });

    //  // Set the next notification.
    //  var days = reminderLevelToDays[storage.engine.reminderLevel];
    //  storage.engine.nextNotified = Date.now() * (days * 1440 * 60000);

    //  // Convert the new date to milliseconds.
    //  var milliseconds = storage.engine.nextNotified;

    //  // Set the next timeout.
    //  timeout = timers.setTimeout(showNotification, Date.now() - milliseconds);
    //};

    //// If this notification is scheduled for the future, set a timeout.
    //if (nextNotified > 0) {
    //  // Set a timeout with the difference until the notification should
    //  // trigger.
    //  timeout = timers.setTimeout(showNotification, Date.now() - nextNotified);
    //}
    //// Otherwise immediately show and schedule for the next one.
    //else {
    //  showNotification();
    //}
  }
}
<commit_msg>looking for notif repeat bug<commit_after>import { environment } from './environment';
import storage from './storage';

export var toDays = [
  // Daily.
  1,
  // Half weekly.
  3.5,
  // Weekly.
  7,
  // Bi-weekly.
  14,
  // Monthly.
  30
];

/**
 * Schedule a new notification.
 *
 * @param {number} when - time as a moment object or unix timestamp.
 * @param {number} days - how many days until the next notification.
 */
export function create(name, when, days) {
  var minutes = null;

  if (environment === 'chrome') {
    // Convert the repeating days to minutes.
    minutes = days * (24 * 60);

    // When using the same name, Chrome will automatically clear out the previous
    // notification.
    chrome.alarms.create(name, {
      when: Number(when),
      periodInMinutes: minutes
    });
  }
  // In Firefox, the extension library cannot trigger a notification, so
  // instead we let the background script handle it.  Just need to let the
  // extension know when the right time is to trigger.
  else if (environment === 'firefox') {
    self.port.emit('notification.set', when);
  }
}

export function clear(name) {
	if (environment === 'chrome') {
		chrome.alarms.clear(name);
	}
}

export function get1(name) {
	if (environment === 'chrome') {
		return chrome.alarms.get;
	}
}

export function get2(name, callback) {
	chrome.alarms.get(name, function(alarm) { callback(alarm); }
	);
}

export function get(name) {
	return new Promise(function(resolve) {
		chrome.alarms.get(name, resolve);
	});
}

/**
 * Allows to create notification manually, used for thresholdreminders
 */
export function notify(name, type, amount) {

  if (environment === 'chrome') {
    chrome.notifications.create(name, {
      type: 'basic',
      iconUrl: '../img/logo64.png',
      title: 'Tipsy',
      message: 'Time to donate! You have reached your '+ type + ' threshold with an amount of $' + amount + '.'
    }, function unhandledCallback() {});
  } else if (environment === 'firefox') {
    //TODO implement me
    console.error('Threshold notification for Firefox not yet implemented.');
  }
}

function addClickable() {
  chrome.notifications.onClicked.addListener(function(notificationId, buttonIndex) {
    alert("clicked!");
  })
}
/**
 * Listens for Chrome alarms to trigger the next notification.
 */
export function listen(worker) {
  if (environment === 'chrome') {
    storage.get('settings').then(function(settings) {
      var createNotification = function() {
        // Once the alarm triggers, create a notification to dispaly to the
        // user.
        if (settings.moneyIsOwed) {
          console.log('creating');
          chrome.notifications.create("tipsy", {
            type: 'basic',
            iconUrl: '../img/logo64.png',
            title: 'Tipsy',
            message: 'Time to Donate!'
          }, function unhandledCallback() {
            // Reset the next notified in the storage engine.
            var days = toDays[settings.reminderLevel];
            var next = new Date(settings.nextNotified);
            next.setDate(next.getDate() + days);
            settings.nextNotified = Number(next);

            // Create the next alarm.
            create('tipsy', next, days);

            storage.set('settings', settings);
          });
        addClickable()
        };
      }
      if (Number(settings.nextNotified) < Date.now()) {
        createNotification();
      }

      chrome.alarms.onAlarm.addListener(function() {
        createNotification();
      });
    });
  }

  else if (environment === 'firefox') {
    var notifications = require('sdk/notifications');
    var timers = require('timers');

    // Create a notification timeout upon launching that figures out when, the
    // next reminder should trigger.
    var timeout;

    // Cache this value for easier access.
    var nextNotified = storage.engine.nextNotified;

    // Reset the current notificaiton.
    worker.port.on('notification.set', function(days) {
      timers.clearTimeout(timeout);

      // Cache this value for easier access.
      nextNotified = storage.engine.nextNotified;
      //timeout = setTimeout(showNotification, Date.now() - nextNotified);
    });

    // Reusable function to show the extension and reset.
    //var showNotification = function() {
    //  // Always reset the timeout.
    //  timers.clearTimeout(timeout);

    //  notifications.notify({
    //    title: 'Tipsy',
    //    text: 'Time to donate!'
    //  });

    //  // Set the next notification.
    //  var days = reminderLevelToDays[storage.engine.reminderLevel];
    //  storage.engine.nextNotified = Date.now() * (days * 1440 * 60000);

    //  // Convert the new date to milliseconds.
    //  var milliseconds = storage.engine.nextNotified;

    //  // Set the next timeout.
    //  timeout = timers.setTimeout(showNotification, Date.now() - milliseconds);
    //};

    //// If this notification is scheduled for the future, set a timeout.
    //if (nextNotified > 0) {
    //  // Set a timeout with the difference until the notification should
    //  // trigger.
    //  timeout = timers.setTimeout(showNotification, Date.now() - nextNotified);
    //}
    //// Otherwise immediately show and schedule for the next one.
    //else {
    //  showNotification();
    //}
  }
}
<|endoftext|>


<commit_before>import './styles.scss'

import React, { useEffect } from 'react'
import { useActions, useValues } from 'kea'
import { Layout } from 'react-flex-layout'

import { Input } from 'antd'

import Connection from '../connection'

import explorerLogic from 'scenes/explorer/logic'
import Models from './models'
import SelectedModel from './selected-model'

export default function Sidebar () {
  const { search, selectedModel, selectedKey } = useValues(explorerLogic)
  const { setSearch, focusSearch, moveSelectionUp, moveSelectionDown, enterSelection } = useActions(explorerLogic)

  // eslint-disable-next-line
  useEffect(() => {
    window.setTimeout(focusSearch, 100)
  }, [])
  useEffect(() => {
    window.setTimeout(() => {
      const element = document.querySelector('.ant-tree-node-selected')
      element && element.scrollIntoView({behavior: "auto", block: "nearest"})
    }, 100)
  }, [selectedKey])

  return (
    <Layout>
      <Layout layoutHeight={100}>

            <Connection />

            <Input.Search
              id='sidebar-model-field-search'
              placeholder='Type to search...'
              autoComplete="off"
              value={search}
              onKeyDown={e => {
                if (e.keyCode === 13 || e.keyCode === 38 || e.keyCode === 40) {
                  e.preventDefault()
                  if (e.keyCode === 38) {
                    moveSelectionUp()
                  }
                  if (e.keyCode === 40) {
                    moveSelectionDown()
                  }
                  if (e.keyCode === 13) {
                    enterSelection()
                  }
                }
              }}
              onChange={e => setSearch(e.target.value)}
            />

      </Layout>
      <Layout layoutHeight='flex'>
        <div className={`explorer-tree${selectedModel ? ' selected-model' : ''}`}>
          {selectedModel ? (
            <SelectedModel />
          ) : (
            <Models />
          )}

      </Layout>
    </Layout>
  )
}
<commit_msg>backspace goes up<commit_after>import './styles.scss'

import React, { useEffect } from 'react'
import { useActions, useValues } from 'kea'
import { Layout } from 'react-flex-layout'

import { Input } from 'antd'

import Connection from '../connection'

import explorerLogic from 'scenes/explorer/logic'
import Models from './models'
import SelectedModel from './selected-model'

export default function Sidebar () {
  const { search, selectedModel, selectedKey } = useValues(explorerLogic)
  const { setSearch, focusSearch, moveSelectionUp, moveSelectionDown, enterSelection, closeModel, setSelectedKey } = useActions(explorerLogic)

  // eslint-disable-next-line
  useEffect(() => {
    window.setTimeout(focusSearch, 100)
  }, [])
  useEffect(() => {
    window.setTimeout(() => {
      const element = document.querySelector('.ant-tree-node-selected')
      element && element.scrollIntoView({behavior: "auto", block: "nearest"})
    }, 100)
  }, [selectedKey])

  return (
    <Layout>
      <Layout layoutHeight={100}>

            <Connection />

            <Input.Search
              id='sidebar-model-field-search'
              placeholder='Type to search...'
              autoComplete="off"
              value={search}
              onKeyDown={e => {
                if (e.keyCode === 8) {
                  if (search === '') {
                    e.preventDefault()
                    if (selectedKey === selectedModel) {
                      closeModel(selectedModel)
                    } else {
                      setSelectedKey(selectedModel)
                    }
                  }
                }
                if (e.keyCode === 13 || e.keyCode === 38 || e.keyCode === 40) {
                  e.preventDefault()
                  if (e.keyCode === 38) {
                    moveSelectionUp()
                  }
                  if (e.keyCode === 40) {
                    moveSelectionDown()
                  }
                  if (e.keyCode === 13) {
                    enterSelection()
                  }
                }
              }}
              onChange={e => setSearch(e.target.value)}
            />

      </Layout>
      <Layout layoutHeight='flex'>
        <div className={`explorer-tree${selectedModel ? ' selected-model' : ''}`}>
          {selectedModel ? (
            <SelectedModel />
          ) : (
            <Models />
          )}

      </Layout>
    </Layout>
  )
}
<|endoftext|>


<commit_before>/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

module.exports = function(RED) {
    "use strict";
    var spawn = require('child_process').spawn;
    var exec = require('child_process').exec;
    var isUtf8 = require('is-utf8');

    function ExecNode(n) {
        RED.nodes.createNode(this,n);
        this.cmd = (n.command || "").trim();
        if (n.addpay === undefined) { n.addpay = true; }
        this.addpay = n.addpay;
        this.append = (n.append || "").trim();
        this.useSpawn = (n.useSpawn == "true");
        this.timer = Number(n.timer || 0)*1000;
        this.activeProcesses = {};
        this.oldrc = (n.oldrc || false).toString();
        var node = this;

        var cleanup = function(p) {
            node.activeProcesses[p].kill();
            //node.status({fill:"red",shape:"dot",text:"timeout"});
            //node.error("Exec node timeout");
        }

        this.on("input", function(msg) {
            if (msg.hasOwnProperty("kill")) {
                if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = "SIGTERM"; }
                if (msg.hasOwnProperty("pid")) {
                    if (node.activeProcesses.hasOwnProperty(msg.pid) ) {
                        node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase());
                        node.status({fill:"red",shape:"dot",text:"killed"});
                    }
                }
                else {
                    if (Object.keys(node.activeProcesses).length === 1) {
                        node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase());
                        node.status({fill:"red",shape:"dot",text:"killed"});
                    }
                }
            }
            else {
                var child;
                if (this.useSpawn === true) {
                    // make the extra args into an array
                    // then prepend with the msg.payload
                    var arg = node.cmd;
                    if ((node.addpay === true) && msg.hasOwnProperty("payload")) { arg += " "+msg.payload; }
                    if (node.append.trim() !== "") { arg += " "+node.append; }
                    // slice whole line by spaces and removes any quotes since spawn can't handle them
                    arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g).map((a) => {
                        if (/^".*"$/.test(a)) {
                            return a.slice(1,-1)
                        } else {
                            return a
                        }
                    });
                    var cmd = arg.shift();
                    /* istanbul ignore else  */
                    if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
                    child = spawn(cmd,arg);
                    node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
                    var unknownCommand = (child.pid === undefined);
                    if (node.timer !== 0) {
                        child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                    }
                    node.activeProcesses[child.pid] = child;
                    child.stdout.on('data', function (data) {
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            // console.log('[exec] stdout: ' + data,child.pid);
                            if (isUtf8(data)) { msg.payload = data.toString(); }
                            else { msg.payload = data; }
                            node.send([RED.util.cloneMessage(msg),null,null]);
                        }
                    });
                    child.stderr.on('data', function (data) {
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            if (isUtf8(data)) { msg.payload = data.toString(); }
                            else { msg.payload = Buffer.from(data); }
                            node.send([null,RED.util.cloneMessage(msg),null]);
                        }
                    });
                    child.on('close', function (code,signal) {
                        if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) {
                            delete node.activeProcesses[child.pid];
                            if (child.tout) { clearTimeout(child.tout); }
                            msg.payload = code;
                            if (node.oldrc === "false") {
                                msg.payload = {code:code};
                                if (signal) { msg.payload.signal = signal; }
                            }
                            if (code === 0) { node.status({}); }
                            if (code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
                            else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); }
                            else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); }
                            node.send([null,null,RED.util.cloneMessage(msg)]);
                        }
                    });
                    child.on('error', function (code) {
                        if (child.tout) { clearTimeout(child.tout); }
                        delete node.activeProcesses[child.pid];
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            node.error(code,RED.util.cloneMessage(msg));
                        }
                    });
                }
                else {
                    var cl = node.cmd;
                    if ((node.addpay === true) && msg.hasOwnProperty("payload")) { cl += " "+msg.payload; }
                    if (node.append.trim() !== "") { cl += " "+node.append; }
                    /* istanbul ignore else  */
                    if (RED.settings.verbose) { node.log(cl); }
                    child = exec(cl, {encoding:'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
                        var msg2, msg3;
                        delete msg.payload;
                        if (stderr) {
                            msg2 = RED.util.cloneMessage(msg);
                            msg2.payload = stderr;
                        }
                        msg.payload = Buffer.from(stdout,"binary");
                        if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
                        node.status({});
                        //console.log('[exec] stdout: ' + stdout);
                        //console.log('[exec] stderr: ' + stderr);
                        if (error !== null) {
                            msg3 = {payload:{code:error.code, message:error.message}};
                            if (error.signal) { msg3.payload.signal = error.signal; }
                            if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
                            else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
                            node.log('error:' + error);
                        }
                        else if (node.oldrc === "false") {
                            msg3 = {payload:{code:0}};
                        }
                        if (!msg3) { node.status({}); }
                        else {
                            msg.rc = msg3.payload;
                            if (msg2) { msg2.rc = msg3.payload; }
                        }
                        node.send([msg,msg2,msg3]);
                        if (child.tout) { clearTimeout(child.tout); }
                        delete node.activeProcesses[child.pid];
                    });
                    node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
                    child.on('error',function() {});
                    if (node.timer !== 0) {
                        child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                    }
                    node.activeProcesses[child.pid] = child;
                }
            }
        });

        this.on('close',function() {
            for (var pid in node.activeProcesses) {
                /* istanbul ignore else  */
                if (node.activeProcesses.hasOwnProperty(pid)) {
                    if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); }
                    // console.log("KILLING",pid);
                    var process = node.activeProcesses[pid];
                    node.activeProcesses[pid] = null;
                    process.kill();
                }
            }
            node.activeProcesses = {};
            node.status({});
        });
    }
    RED.nodes.registerType("exec",ExecNode);
}
<commit_msg>Exec: Preserve existing properties on msg object<commit_after>/**
 * Copyright JS Foundation and other contributors, http://js.foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

module.exports = function(RED) {
    "use strict";
    var spawn = require('child_process').spawn;
    var exec = require('child_process').exec;
    var isUtf8 = require('is-utf8');

    function ExecNode(n) {
        RED.nodes.createNode(this,n);
        this.cmd = (n.command || "").trim();
        if (n.addpay === undefined) { n.addpay = true; }
        this.addpay = n.addpay;
        this.append = (n.append || "").trim();
        this.useSpawn = (n.useSpawn == "true");
        this.timer = Number(n.timer || 0)*1000;
        this.activeProcesses = {};
        this.oldrc = (n.oldrc || false).toString();
        var node = this;

        var cleanup = function(p) {
            node.activeProcesses[p].kill();
            //node.status({fill:"red",shape:"dot",text:"timeout"});
            //node.error("Exec node timeout");
        }

        this.on("input", function(msg) {
            if (msg.hasOwnProperty("kill")) {
                if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = "SIGTERM"; }
                if (msg.hasOwnProperty("pid")) {
                    if (node.activeProcesses.hasOwnProperty(msg.pid) ) {
                        node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase());
                        node.status({fill:"red",shape:"dot",text:"killed"});
                    }
                }
                else {
                    if (Object.keys(node.activeProcesses).length === 1) {
                        node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase());
                        node.status({fill:"red",shape:"dot",text:"killed"});
                    }
                }
            }
            else {
                var child;
                if (this.useSpawn === true) {
                    // make the extra args into an array
                    // then prepend with the msg.payload
                    var arg = node.cmd;
                    if ((node.addpay === true) && msg.hasOwnProperty("payload")) { arg += " "+msg.payload; }
                    if (node.append.trim() !== "") { arg += " "+node.append; }
                    // slice whole line by spaces and removes any quotes since spawn can't handle them
                    arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g).map((a) => {
                        if (/^".*"$/.test(a)) {
                            return a.slice(1,-1)
                        } else {
                            return a
                        }
                    });
                    var cmd = arg.shift();
                    /* istanbul ignore else  */
                    if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
                    child = spawn(cmd,arg);
                    node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
                    var unknownCommand = (child.pid === undefined);
                    if (node.timer !== 0) {
                        child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                    }
                    node.activeProcesses[child.pid] = child;
                    child.stdout.on('data', function (data) {
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            // console.log('[exec] stdout: ' + data,child.pid);
                            if (isUtf8(data)) { msg.payload = data.toString(); }
                            else { msg.payload = data; }
                            node.send([RED.util.cloneMessage(msg),null,null]);
                        }
                    });
                    child.stderr.on('data', function (data) {
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            if (isUtf8(data)) { msg.payload = data.toString(); }
                            else { msg.payload = Buffer.from(data); }
                            node.send([null,RED.util.cloneMessage(msg),null]);
                        }
                    });
                    child.on('close', function (code,signal) {
                        if (unknownCommand || (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null)) {
                            delete node.activeProcesses[child.pid];
                            if (child.tout) { clearTimeout(child.tout); }
                            msg.payload = code;
                            if (node.oldrc === "false") {
                                msg.payload = {code:code};
                                if (signal) { msg.payload.signal = signal; }
                            }
                            if (code === 0) { node.status({}); }
                            if (code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
                            else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc:"+code}); }
                            else { node.status({fill:"yellow",shape:"dot",text:"rc:"+code}); }
                            node.send([null,null,RED.util.cloneMessage(msg)]);
                        }
                    });
                    child.on('error', function (code) {
                        if (child.tout) { clearTimeout(child.tout); }
                        delete node.activeProcesses[child.pid];
                        if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
                            node.error(code,RED.util.cloneMessage(msg));
                        }
                    });
                }
                else {
                    var cl = node.cmd;
                    if ((node.addpay === true) && msg.hasOwnProperty("payload")) { cl += " "+msg.payload; }
                    if (node.append.trim() !== "") { cl += " "+node.append; }
                    /* istanbul ignore else  */
                    if (RED.settings.verbose) { node.log(cl); }
                    child = exec(cl, {encoding:'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
                        var msg2, msg3;
                        delete msg.payload;
                        if (stderr) {
                            msg2 = RED.util.cloneMessage(msg);
                            msg2.payload = stderr;
                        }
                        msg.payload = Buffer.from(stdout,"binary");
                        if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
                        node.status({});
                        //console.log('[exec] stdout: ' + stdout);
                        //console.log('[exec] stderr: ' + stderr);
                        if (error !== null) {
                            msg3 = RED.util.cloneMessage(msg);
                            msg3.payload = {code:error.code, message:error.message};
                            if (error.signal) { msg3.payload.signal = error.signal; }
                            if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
                            else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
                            node.log('error:' + error);
                        }
                        else if (node.oldrc === "false") {
                            msg3 = RED.util.cloneMessage(msg);
                            msg3.payload = {code:0};
                        }
                        if (!msg3) { node.status({}); }
                        else {
                            msg.rc = msg3.payload;
                            if (msg2) { msg2.rc = msg3.payload; }
                        }
                        node.send([msg,msg2,msg3]);
                        if (child.tout) { clearTimeout(child.tout); }
                        delete node.activeProcesses[child.pid];
                    });
                    node.status({fill:"blue",shape:"dot",text:"pid:"+child.pid});
                    child.on('error',function() {});
                    if (node.timer !== 0) {
                        child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
                    }
                    node.activeProcesses[child.pid] = child;
                }
            }
        });

        this.on('close',function() {
            for (var pid in node.activeProcesses) {
                /* istanbul ignore else  */
                if (node.activeProcesses.hasOwnProperty(pid)) {
                    if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); }
                    // console.log("KILLING",pid);
                    var process = node.activeProcesses[pid];
                    node.activeProcesses[pid] = null;
                    process.kill();
                }
            }
            node.activeProcesses = {};
            node.status({});
        });
    }
    RED.nodes.registerType("exec",ExecNode);
}
<|endoftext|>