<commit_before>// TODO :
// - add hooks for incoming and outgoing records of specific types.
// - create package for ember models, complete with pluggable triggers fired off the hooks
//

import Ember from 'ember';
import DS from 'ember-data';
import Where from 'npm:where-clause-evaluate';

const DEFAULT_NAMESPACE = 'DS.LSAdapter';

const LSAdapter = DS.Adapter.extend(Ember.Evented, {
  /**
   * This governs if promises will be resolved immeadiatly for `findAll`
   * requests or if they will wait for the store requests to finish. This matches
   * the ember < 2.0 behavior.
   * [deprecation id: ds.adapter.should-reload-all-default-behavior]
   */
  shouldReloadAll: function(/* modelClass, snapshotArray */) {
    return true;
  },

  /**
   * Conforms to ember <2.0 behavior, in order to remove deprecation.
   * Probably safe to remove if running on ember 2.0
   * [deprecation id: ds.model.relationship-changing-to-asynchrounous-by-default]
   */
  shouldBackgroundReloadRecord: function(){
    return false;
  },

  /**
    This is the main entry point into finding records. The first parameter to
    this method is the model's name as a string.

    @method find
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
    */
  findRecord: function(store, type, id, opts) {
    var allowRecursive = true;
    var namespace = this._namespaceForType(type);
    var record = Ember.A(namespace.records[id]);

    /**
     * In the case where there are relationships, this method is called again
     * for each relation. Given the relations have references to the main
     * object, we use allowRecursive to avoid going further into infinite
     * recursiveness.
     *
     * Concept from ember-indexdb-adapter
     */
    if (opts && typeof opts.allowRecursive !== 'undefined') {
      allowRecursive = opts.allowRecursive;
    }

    if (!record || !record.hasOwnProperty('id')) {
      return Ember.RSVP.reject(new Error("Couldn't find record of" + " type '" + type.modelName + "' for the id '" + id + "'."));
    }

    if (allowRecursive) {
      return this.loadRelationships(store, type, record);
    } else {
      return Ember.RSVP.resolve(record);
    }
  },

  findMany: function (store, type, ids, opts) {
    var namespace = this._namespaceForType(type);
    var allowRecursive = true,
      results = Ember.A([]), record;

    /**
     * In the case where there are relationships, this method is called again
     * for each relation. Given the relations have references to the main
     * object, we use allowRecursive to avoid going further into infinite
     * recursiveness.
     *
     * Concept from ember-indexdb-adapter
     */
    if (opts && typeof opts.allowRecursive !== 'undefined') {
      allowRecursive = opts.allowRecursive;
    }

    for (var i = 0; i < ids.length; i++) {
      record = namespace.records[ids[i]];
      if (!record || !record.hasOwnProperty('id')) {
        return Ember.RSVP.reject(new Error("Couldn't find record of type '" + type.modelName + "' for the id '" + ids[i] + "'."));
      }
      results.push(Ember.copy(record));
    }

    if (results.get('length') && allowRecursive) {
      return this.loadRelationshipsForMany(store, type, results);
    } else {
      return Ember.RSVP.resolve(results);
    }
  },

  // Supports queries that look like this:
  //
  //   {
  //     <property to query>: <value or regex (for strings) to match>,
  //     ...
  //   }
  //
  // Every property added to the query is an "AND" query, not "OR"
  //
  // Example:
  //
  //  match records with "complete: true" and the name "foo" or "bar"
  //
  //    { complete: true, name: /foo|bar/ }
  query: function (store, type, query /*recordArray*/) {
    var namespace = this._namespaceForType(type);
    var results = this._query(namespace.records, query);

    if (results.get('length')) {
      return this.loadRelationshipsForMany(store, type, results);
    } else {
      return Ember.RSVP.reject();
    }
  },

  _evaluate: Where.newEvaluator({ cache : true }),

  _query: function (records, query) {
    var results = Ember.A([]), record;

    for (var id in records) {
      record = records[id];
      if (!query || this._evaluate(record, query)) {
        results.push(Ember.copy(record));
      }
    }
    return results;
  },

  findAll: function (store, type) {
    var namespace = this._namespaceForType(type),
      results = Ember.A([]);

    for (var id in namespace.records) {
      results.push(Ember.copy(namespace.records[id]));
    }
    return Ember.RSVP.resolve(results);
  },

  createRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var serializer = store.serializerFor(type.modelName);
    var recordHash = serializer.serialize(snapshot, {includeId: true});

    namespaceRecords.records[recordHash.id] = recordHash;

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  updateRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var id = snapshot.id;
    var serializer = store.serializerFor(type.modelName);

    namespaceRecords.records[id] = serializer.serialize(snapshot, {includeId: true});

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  deleteRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var id = snapshot.id;

    delete namespaceRecords.records[id];

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  generateIdForRecord: function () {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  adapterNamespace: function () {
    return this.get('namespace') || DEFAULT_NAMESPACE;
  },

  loadData: function () {
    var storage = this.getLocalStorage().getItem(this.adapterNamespace());
    return storage ? JSON.parse(storage) : {};
  },

  persistData: function(type, data) {
    var modelNamespace = this.modelNamespace(type);
    var localStorageData = this.loadData();

    localStorageData[modelNamespace] = data;

    this.getLocalStorage().setItem(this.adapterNamespace(), JSON.stringify(localStorageData));
  },

  getLocalStorage: function() {
    if (this._localStorage) { return this._localStorage; }

    var storage;
    try {
      storage = this.getNativeStorage() || this._enableInMemoryStorage();
    } catch (e) {
      storage = this._enableInMemoryStorage(e);
    }
    this._localStorage = storage;
    return this._localStorage;
  },

  _enableInMemoryStorage: function(reason) {
    this.trigger('persistenceUnavailable', reason);
    return {
      storage: {},
      getItem: function(name) {
        return this.storage[name];
      },
      setItem: function(name, value) {
        this.storage[name] = value;
      }
    };
  },

  // This exists primarily as a testing extension point
  getNativeStorage: function() {
    return localStorage;
  },

  _namespaceForType: function (type) {
    var namespace = this.modelNamespace(type);
    var storage   = this.loadData();

    return storage[namespace] || {records: {}};
  },

  modelNamespace: function(type) {
    return type.url || type.modelName;
  },

  /**
   * This takes a record, then analyzes the model relationships and replaces
   * ids with the actual values.
   *
   * Stolen from ember-indexdb-adapter
   *
   * Consider the following JSON is entered:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   * }
   *
   * This will return:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   *
   *   "_embedded": {
   *     "comment": [{
   *       "_id": 1,
   *       "comment_title": "FIRST"
   *     }, {
   *       "_id": 2,
   *       "comment_title": "Rails is unagi"
   *     }]
   *   }
   * }
   *
   * This way, whenever a resource returned, its relationships will be also
   * returned.
   *
   * @method loadRelationships
   * @private
   * @param {DS.Model} type
   * @param {Object} record
   */
  loadRelationships: function(store, type, record) {
    var adapter = this,
      relationshipNames, relationships;

    /**
     * Create a chain of promises, so the relationships are
     * loaded sequentially.  Think of the variable
     * `recordPromise` as of the accumulator in a left fold.
     */
    var recordPromise = Ember.RSVP.resolve(record);

    relationshipNames = Ember.get(type, 'relationshipNames');
    relationships = relationshipNames.belongsTo
    .concat(relationshipNames.hasMany);

    relationships.forEach(function(relationName) {
      var relationModel = type.typeForRelationship(relationName,store);
      var relationEmbeddedId = record[relationName];
      var relationProp  = adapter.relationshipProperties(type, relationName);
      var relationType  = relationProp.kind;

      var opts = {allowRecursive: false};

      /**
       * embeddedIds are ids of relations that are included in the main
       * payload, such as:
       *
       * {
       *    cart: {
       *      id: "s85fb",
       *      customer: "rld9u"
       *    }
       * }
       *
       * In this case, cart belongsTo customer and its id is present in the
       * main payload. We find each of these records and add them to _embedded.
       */
      if (relationEmbeddedId && LSAdapter.prototype.isPrototypeOf(adapter))
        {
          recordPromise = recordPromise.then(function(recordPayload) {
            var promise;
            if (relationType === 'belongsTo' || relationType === 'hasOne') {
              promise = adapter.findRecord(null, relationModel, relationEmbeddedId, opts);
            } else if (relationType === 'hasMany') {
              promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts);
            }

            return promise.then(function(relationRecord) {
              return adapter.addEmbeddedPayload(recordPayload, relationName, relationRecord);
            });
          });
        }
    });

    return recordPromise;
  },

  /**
   * Given the following payload,
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      }
   *   }
   *
   * With `relationshipName` being `customer` and `relationshipRecord`
   *
   *   {id: "2", name: "Rambo"}
   *
   * This method returns the following payload:
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      },
   *      _embedded: {
   *        customer: {
   *          id: "2",
   *          name: "Rambo"
   *        }
   *      }
   *   }
   *
   * which is then treated by the serializer later.
   *
   * @method addEmbeddedPayload
   * @private
   * @param {Object} payload
   * @param {String} relationshipName
   * @param {Object} relationshipRecord
   */
  addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
    var objectHasId = (relationshipRecord && relationshipRecord.id);
    var arrayHasIds = (relationshipRecord.length && relationshipRecord.isEvery("id"));
    var isValidRelationship = (objectHasId || arrayHasIds);

    if (isValidRelationship) {
      if (!payload._embedded) {
        payload._embedded = {};
      }

      payload._embedded[relationshipName] = relationshipRecord;
      if (relationshipRecord.length) {
        payload[relationshipName] = relationshipRecord.mapBy('id');
      } else {
        payload[relationshipName] = relationshipRecord.id;
      }
    }

    if (this.isArray(payload[relationshipName])) {
      payload[relationshipName] = payload[relationshipName].filter(function(id) {
        return id;
      });
    }

    return payload;
  },

  isArray: function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  },

  /**
   * Same as `loadRelationships`, but for an array of records.
   *
   * @method loadRelationshipsForMany
   * @private
   * @param {DS.Model} type
   * @param {Object} recordsArray
   */
  loadRelationshipsForMany: function(store, type, recordsArray) {
    var adapter = this,
      promise = Ember.RSVP.resolve(Ember.A([]));

    /**
     * Create a chain of promises, so the records are loaded sequentially.
     * Think of the variable promise as of the accumulator in a left fold.
     */
    recordsArray.forEach(function(record) {
      promise = promise.then(function(records) {
        return adapter.loadRelationships(store, type, record)
        .then(function(loadedRecord) {
          records.push(loadedRecord);
          return records;
        });
      });
    });

    return promise;
  },

  /**
   *
   * @method relationshipProperties
   * @private
   * @param {DS.Model} type
   * @param {String} relationName
   */
  relationshipProperties: function(type, relationName) {
    var relationships = Ember.get(type, 'relationshipsByName');
    if (relationName) {
      return relationships.get(relationName);
    } else {
      return relationships;
    }
  }
});

export default LSAdapter;
<commit_msg>minor comments<commit_after>// TODO :
// - add hooks for incoming and outgoing records of specific types.
// - create package for ember models, complete with pluggable triggers fired off the hooks

import Ember from 'ember';
import DS from 'ember-data';
import Where from 'npm:where-clause-evaluate';

const DEFAULT_NAMESPACE = 'DS.LSAdapter';

const LSAdapter = DS.Adapter.extend(Ember.Evented, {
  /**
   * This governs if promises will be resolved immeadiatly for `findAll`
   * requests or if they will wait for the store requests to finish. This matches
   * the ember < 2.0 behavior.
   * [deprecation id: ds.adapter.should-reload-all-default-behavior]
   */
  shouldReloadAll: function(/* modelClass, snapshotArray */) {
    return true;
  },

  /**
   * Conforms to ember <2.0 behavior, in order to remove deprecation.
   * Probably safe to remove if running on ember 2.0
   * [deprecation id: ds.model.relationship-changing-to-asynchrounous-by-default]
   */
  shouldBackgroundReloadRecord: function(){
    return false;
  },

  /**
    This is the main entry point into finding records. The first parameter to
    this method is the model's name as a string.

    @method find
    @param {DS.Model} type
    @param {Object|String|Integer|null} id
    */
  findRecord: function(store, type, id, opts) {
    var allowRecursive = true;
    var namespace = this._namespaceForType(type);
    var record = Ember.A(namespace.records[id]);

    /**
     * In the case where there are relationships, this method is called again
     * for each relation. Given the relations have references to the main
     * object, we use allowRecursive to avoid going further into infinite
     * recursiveness.
     *
     * Concept from ember-indexdb-adapter
     */
    if (opts && typeof opts.allowRecursive !== 'undefined') {
      allowRecursive = opts.allowRecursive;
    }

    if (!record || !record.hasOwnProperty('id')) {
      return Ember.RSVP.reject(new Error("Couldn't find record of" + " type '" + type.modelName + "' for the id '" + id + "'."));
    }

    if (allowRecursive) {
      return this.loadRelationships(store, type, record);
    } else {
      return Ember.RSVP.resolve(record);
    }
  },

  findMany: function (store, type, ids, opts) {
    var namespace = this._namespaceForType(type);
    var allowRecursive = true,
      results = Ember.A([]), record;

    /**
     * In the case where there are relationships, this method is called again
     * for each relation. Given the relations have references to the main
     * object, we use allowRecursive to avoid going further into infinite
     * recursiveness.
     *
     * Concept from ember-indexdb-adapter
     */
    if (opts && typeof opts.allowRecursive !== 'undefined') {
      allowRecursive = opts.allowRecursive;
    }

    for (var i = 0; i < ids.length; i++) {
      record = namespace.records[ids[i]];
      if (!record || !record.hasOwnProperty('id')) {
        return Ember.RSVP.reject(new Error("Couldn't find record of type '" + type.modelName + "' for the id '" + ids[i] + "'."));
      }
      results.push(Ember.copy(record));
    }

    if (results.get('length') && allowRecursive) {
      return this.loadRelationshipsForMany(store, type, results);
    } else {
      return Ember.RSVP.resolve(results);
    }
  },

  // Supports queries that look like this:
  //
  //   {
  //     <property to query>: <value or regex (for strings) to match>,
  //     ...
  //   }
  //
  // Every property added to the query is an "AND" query, not "OR"
  //
  // Example:
  //
  //  match records with "complete: true" and the name "foo" or "bar"
  //
  //    { complete: true, name: /foo|bar/ }
  query: function (store, type, query /*recordArray*/) {
    var namespace = this._namespaceForType(type);
    var results = this._query(namespace.records, query);

    if (results.get('length')) {
      return this.loadRelationshipsForMany(store, type, results);
    } else {
      return Ember.RSVP.reject();
    }
  },

  _evaluate: Where.newEvaluator({ cache : true }),

  _query: function (records, query) {
    var results = Ember.A([]), record;

    for (var id in records) {
      record = records[id];
      if (!query || this._evaluate(record, query)) {
        results.push(Ember.copy(record));
      }
    }
    return results;
  },

  findAll: function (store, type) {
    var namespace = this._namespaceForType(type),
      results = Ember.A([]);

    for (var id in namespace.records) {
      results.push(Ember.copy(namespace.records[id]));
    }
    return Ember.RSVP.resolve(results);
  },

  createRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var serializer = store.serializerFor(type.modelName);
    var recordHash = serializer.serialize(snapshot, {includeId: true});

    namespaceRecords.records[recordHash.id] = recordHash;

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  updateRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var id = snapshot.id;
    var serializer = store.serializerFor(type.modelName);

    namespaceRecords.records[id] = serializer.serialize(snapshot, {includeId: true});

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  deleteRecord: function (store, type, snapshot) {
    var namespaceRecords = this._namespaceForType(type);
    var id = snapshot.id;

    delete namespaceRecords.records[id];

    this.persistData(type, namespaceRecords);
    return Ember.RSVP.resolve();
  },

  generateIdForRecord: function () {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  adapterNamespace: function () {
    return this.get('namespace') || DEFAULT_NAMESPACE;
  },

  loadData: function () {
    var storage = this.getLocalStorage().getItem(this.adapterNamespace());
    return storage ? JSON.parse(storage) : {};
  },

  persistData: function(type, data) {
    var modelNamespace = this.modelNamespace(type);
    var localStorageData = this.loadData();

    localStorageData[modelNamespace] = data;

    this.getLocalStorage().setItem(this.adapterNamespace(), JSON.stringify(localStorageData));
  },

  getLocalStorage: function() {
    if (this._localStorage) { return this._localStorage; }

    var storage;
    try {
      storage = this.getNativeStorage() || this._enableInMemoryStorage();
    } catch (e) {
      storage = this._enableInMemoryStorage(e);
    }
    this._localStorage = storage;
    return this._localStorage;
  },

  _enableInMemoryStorage: function(reason) {
    this.trigger('persistenceUnavailable', reason);
    return {
      storage: {},
      getItem: function(name) {
        return this.storage[name];
      },
      setItem: function(name, value) {
        this.storage[name] = value;
      }
    };
  },

  // This exists primarily as a testing extension point
  getNativeStorage: function() {
    return localStorage;
  },

  _namespaceForType: function (type) {
    var namespace = this.modelNamespace(type);
    var storage   = this.loadData();

    return storage[namespace] || {records: {}};
  },

  modelNamespace: function(type) {
    return type.url || type.modelName;
  },

  /**
   * This takes a record, then analyzes the model relationships and replaces
   * ids with the actual values.
   *
   * Stolen from ember-indexdb-adapter
   *
   * Consider the following JSON is entered:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   * }
   *
   * This will return:
   *
   * ```js
   * {
   *   "id": 1,
   *   "title": "Rails Rambo",
   *   "comments": [1, 2]
   *
   *   "_embedded": {
   *     "comment": [{
   *       "_id": 1,
   *       "comment_title": "FIRST"
   *     }, {
   *       "_id": 2,
   *       "comment_title": "Rails is unagi"
   *     }]
   *   }
   * }
   *
   * This way, whenever a resource returned, its relationships will be also
   * returned.
   *
   * @method loadRelationships
   * @private
   * @param {DS.Model} type
   * @param {Object} record
   */
  loadRelationships: function(store, type, record) {
    var adapter = this,
      relationshipNames, relationships;

    /**
     * Create a chain of promises, so the relationships are
     * loaded sequentially.  Think of the variable
     * `recordPromise` as of the accumulator in a left fold.
     */
    var recordPromise = Ember.RSVP.resolve(record);

    relationshipNames = Ember.get(type, 'relationshipNames');
    relationships = relationshipNames.belongsTo
    .concat(relationshipNames.hasMany);

    relationships.forEach(function(relationName) {
      var relationModel = type.typeForRelationship(relationName,store);
      var relationEmbeddedId = record[relationName];
      var relationProp  = adapter.relationshipProperties(type, relationName);
      var relationType  = relationProp.kind;

      var opts = {allowRecursive: false};

      /**
       * embeddedIds are ids of relations that are included in the main
       * payload, such as:
       *
       * {
       *    cart: {
       *      id: "s85fb",
       *      customer: "rld9u"
       *    }
       * }
       *
       * In this case, cart belongsTo customer and its id is present in the
       * main payload. We find each of these records and add them to _embedded.
       */
      if (relationEmbeddedId && LSAdapter.prototype.isPrototypeOf(adapter))
        {
          recordPromise = recordPromise.then(function(recordPayload) {
            var promise;
            if (relationType === 'belongsTo' || relationType === 'hasOne') {
              promise = adapter.findRecord(null, relationModel, relationEmbeddedId, opts);
            } else if (relationType === 'hasMany') {
              promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts);
            }

            return promise.then(function(relationRecord) {
              return adapter.addEmbeddedPayload(recordPayload, relationName, relationRecord);
            });
          });
        }
    });

    return recordPromise;
  },

  /**
   * Given the following payload,
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      }
   *   }
   *
   * With `relationshipName` being `customer` and `relationshipRecord`
   *
   *   {id: "2", name: "Rambo"}
   *
   * This method returns the following payload:
   *
   *   {
   *      cart: {
   *        id: "1",
   *        customer: "2"
   *      },
   *      _embedded: {
   *        customer: {
   *          id: "2",
   *          name: "Rambo"
   *        }
   *      }
   *   }
   *
   * which is then treated by the serializer later.
   *
   * @method addEmbeddedPayload
   * @private
   * @param {Object} payload
   * @param {String} relationshipName
   * @param {Object} relationshipRecord
   */
  addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
    var objectHasId = (relationshipRecord && relationshipRecord.id);
    var arrayHasIds = (relationshipRecord.length && relationshipRecord.isEvery("id"));
    var isValidRelationship = (objectHasId || arrayHasIds);

    if (isValidRelationship) {
      if (!payload._embedded) {
        payload._embedded = {};
      }

      payload._embedded[relationshipName] = relationshipRecord;
      if (relationshipRecord.length) {
        payload[relationshipName] = relationshipRecord.mapBy('id');
      } else {
        payload[relationshipName] = relationshipRecord.id;
      }
    }

    if (this.isArray(payload[relationshipName])) {
      payload[relationshipName] = payload[relationshipName].filter(function(id) {
        return id;
      });
    }

    return payload;
  },

  isArray: function(value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  },

  /**
   * Same as `loadRelationships`, but for an array of records.
   *
   * @method loadRelationshipsForMany
   * @private
   * @param {DS.Model} type
   * @param {Object} recordsArray
   */
  loadRelationshipsForMany: function(store, type, recordsArray) {
    var adapter = this,
      promise = Ember.RSVP.resolve(Ember.A([]));

    /**
     * Create a chain of promises, so the records are loaded sequentially.
     * Think of the variable promise as of the accumulator in a left fold.
     */
    recordsArray.forEach(function(record) {
      promise = promise.then(function(records) {
        return adapter.loadRelationships(store, type, record)
        .then(function(loadedRecord) {
          records.push(loadedRecord);
          return records;
        });
      });
    });

    return promise;
  },

  /**
   *
   * @method relationshipProperties
   * @private
   * @param {DS.Model} type
   * @param {String} relationName
   */
  relationshipProperties: function(type, relationName) {
    var relationships = Ember.get(type, 'relationshipsByName');
    if (relationName) {
      return relationships.get(relationName);
    } else {
      return relationships;
    }
  }
});

export default LSAdapter;
<|endoftext|>


<commit_before>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('model-controller', function(Y) {
  /**
    Provides a collection of utility methods to interact
    with the db and it's models.

    @class ModelController
    @constructor
  */
  var ModelController = Y.Base.create('juju-model-controller', Y.Base, [], {

    /**
      Inits promise object stores.

      @method initializer
    */
    initializer: function() {
      this._charmPromises = {};
      this._servicePromises = {};
      this._serviceCharmPromises = {};
    },

    /**
      Stores promises for a short period of time as a way to avoid multiple
      instantiations when the application dispatches before the promises return

      @method _getPromise
      @param {String} key The id of the charm/service which will be used to
        store and find the promise in the objects.
      @param {Object} store Object which stores the various promises of a
        specific type.
      @param {Function} fn Promise executor function.
      @return {Promise} Stored promise matching the provided key.
    */
    _getPromise: function(key, store, fn) {
      var promise = store[key];
      if (!promise) {
        store[key] = promise = new Promise(fn);
        var cleanUp = function() {
          delete store[key];
        };
        promise.then(cleanUp, cleanUp);
      }
      return promise;
    },

    /**
      Returns a promise for a fully populated charm model.

      @method getCharm
      @param {String} charmId The charmId that you want to populate.
      @return {Promise} A promise for the charm model.
    */
    getCharm: function(charmId) {
      const db = this.get('db');
      const env = this.get('env');
      const charmstore = this.get('charmstore');

      return this._getPromise(
        charmId, this._charmPromises,
        (resolve, reject) => {
          var charm = db.charms.getById(charmId);
          if (charm && charm.loaded) {
            resolve(charm);
          } else {
            charm = db.charms.add({url: charmId}).load(env,
              // If views are bound to the charm model, firing "update" is
              // unnecessary, and potentially even mildly harmful.
              (err, data) => {
                const charm = db.charms.getById(charmId);
                charm.populateFileList(charmstore.getEntity.bind(charmstore), () => {
                  db.fireEvent('update');
                  resolve(charm);
                });
              });
          }
        });
    },

    /**
      Returns a promise for a fully populated service model or an error.

      @method getService
      @param {String} serviceId A string ID for the service to fetch.
      @return {Promise} A promise for fully populated service data.
    */
    getService: function(serviceId) {
      var db = this.get('db'),
          env = this.get('env');

      return this._getPromise(
        serviceId, this._servicePromises,
        function(resolve, reject) {
          // `this` points to the serviceList
          var service = db.services.getById(serviceId);
          // If the service and all data has already been loaded, or if the
          // service is pending, resolve.
          if (service && (service.get('loaded') || service.get('pending'))) {
            resolve(service);
            return;
          }
          if (!service || !service.get('loaded')) {
            env.getApplicationConfig(serviceId, function(result) {
              if (result.err) {
                // The service doesn't exist
                reject(result);
              } else {
                var service = db.services.getById(result.applicationName);
                service.setAttrs({
                  config: result.result.config,
                  constraints: result.result.constraints,
                  loaded: true,
                  series: result.result.series
                });
                resolve(service);
              }
            });
          }
        });
    },

    /**
      Populates the service and charm data for the supplied service id and
      returns a promise that you can use to know when it's ready to go.

      @method getServiceWithCharm
      @param {String} serviceId The service id to populate.
    */
    getServiceWithCharm: function(serviceId) {
      var mController = this;

      return this._getPromise(
        serviceId, this._serviceCharmPromises,
        function(resolve, reject) {
          mController.getService(serviceId).then(function(service) {
            mController.getCharm(service.get('charm')).then(function(charm) {
              resolve({service: service, charm: charm});
            }, reject);
          }, reject);
        });
    }

  }, {
    ATTRS: {
      /**
        Reference to the client db.

        @attribute db
        @type {Y.Base}
        @default undefined
      */
      db: {},

      /**
        Reference to the client env.

        @attribute env
        @type {Y.Base}
        @default undefined
      */
      env: {},

      /**
        Reference to the client charm store.

        @attribute charmstore
        @type {Object}
        @default undefined
      */
      charmstore: {}
    }
  });

  Y.namespace('juju').ModelController = ModelController;

}, '', { requires: ['base-build', 'base'] });
<commit_msg>Only populate the file list on charms from the charmstore.<commit_after>/*
This file is part of the Juju GUI, which lets users view and manage Juju
environments within a graphical interface (https://launchpad.net/juju-gui).
Copyright (C) 2012-2013 Canonical Ltd.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License version 3, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License for more details.

You should have received a copy of the GNU Affero General Public License along
with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

'use strict';

YUI.add('model-controller', function(Y) {
  /**
    Provides a collection of utility methods to interact
    with the db and it's models.

    @class ModelController
    @constructor
  */
  var ModelController = Y.Base.create('juju-model-controller', Y.Base, [], {

    /**
      Inits promise object stores.

      @method initializer
    */
    initializer: function() {
      this._charmPromises = {};
      this._servicePromises = {};
      this._serviceCharmPromises = {};
    },

    /**
      Stores promises for a short period of time as a way to avoid multiple
      instantiations when the application dispatches before the promises return

      @method _getPromise
      @param {String} key The id of the charm/service which will be used to
        store and find the promise in the objects.
      @param {Object} store Object which stores the various promises of a
        specific type.
      @param {Function} fn Promise executor function.
      @return {Promise} Stored promise matching the provided key.
    */
    _getPromise: function(key, store, fn) {
      var promise = store[key];
      if (!promise) {
        store[key] = promise = new Promise(fn);
        var cleanUp = function() {
          delete store[key];
        };
        promise.then(cleanUp, cleanUp);
      }
      return promise;
    },

    /**
      Returns a promise for a fully populated charm model.

      @method getCharm
      @param {String} charmId The charmId that you want to populate.
      @return {Promise} A promise for the charm model.
    */
    getCharm: function(charmId) {
      const db = this.get('db');
      const env = this.get('env');
      const charmstore = this.get('charmstore');

      return this._getPromise(
        charmId, this._charmPromises,
        (resolve, reject) => {
          var charm = db.charms.getById(charmId);
          if (charm && charm.loaded) {
            resolve(charm);
          } else {
            charm = db.charms.add({url: charmId}).load(env,
              // If views are bound to the charm model, firing "update" is
              // unnecessary, and potentially even mildly harmful.
              (err, data) => {
                const charm = db.charms.getById(charmId);
                if (charmId.indexOf('local:') === -1) {
                  charm.populateFileList(
                    charmstore.getEntity.bind(charmstore), () => {
                      db.fireEvent('update');
                      resolve(charm);
                    });
                }
              });
          }
        });
    },

    /**
      Returns a promise for a fully populated service model or an error.

      @method getService
      @param {String} serviceId A string ID for the service to fetch.
      @return {Promise} A promise for fully populated service data.
    */
    getService: function(serviceId) {
      var db = this.get('db'),
          env = this.get('env');

      return this._getPromise(
        serviceId, this._servicePromises,
        function(resolve, reject) {
          // `this` points to the serviceList
          var service = db.services.getById(serviceId);
          // If the service and all data has already been loaded, or if the
          // service is pending, resolve.
          if (service && (service.get('loaded') || service.get('pending'))) {
            resolve(service);
            return;
          }
          if (!service || !service.get('loaded')) {
            env.getApplicationConfig(serviceId, function(result) {
              if (result.err) {
                // The service doesn't exist
                reject(result);
              } else {
                var service = db.services.getById(result.applicationName);
                service.setAttrs({
                  config: result.result.config,
                  constraints: result.result.constraints,
                  loaded: true,
                  series: result.result.series
                });
                resolve(service);
              }
            });
          }
        });
    },

    /**
      Populates the service and charm data for the supplied service id and
      returns a promise that you can use to know when it's ready to go.

      @method getServiceWithCharm
      @param {String} serviceId The service id to populate.
    */
    getServiceWithCharm: function(serviceId) {
      var mController = this;

      return this._getPromise(
        serviceId, this._serviceCharmPromises,
        function(resolve, reject) {
          mController.getService(serviceId).then(function(service) {
            mController.getCharm(service.get('charm')).then(function(charm) {
              resolve({service: service, charm: charm});
            }, reject);
          }, reject);
        });
    }

  }, {
    ATTRS: {
      /**
        Reference to the client db.

        @attribute db
        @type {Y.Base}
        @default undefined
      */
      db: {},

      /**
        Reference to the client env.

        @attribute env
        @type {Y.Base}
        @default undefined
      */
      env: {},

      /**
        Reference to the client charm store.

        @attribute charmstore
        @type {Object}
        @default undefined
      */
      charmstore: {}
    }
  });

  Y.namespace('juju').ModelController = ModelController;

}, '', { requires: ['base-build', 'base'] });
<|endoftext|>


<commit_before>/**
  @module ember-flexberry-designer
*/

import EmberObject from '@ember/object';
import { computed, get } from '@ember/object';
import { A, isArray } from '@ember/array';
import joint from 'npm:jointjs';
import $ from 'jquery';

/**
  An object that defines any primitive on the UML diagram.

  @class FdUmlPrimitive
  @extends Ember.Object
*/
export default EmberObject.extend({

  /**
    The object that describes the primitive.

    @property primitive
    @type Object
  */
  primitive: undefined,

  /**
    The unique identifier of the primitive.

    @property id
    @type String
  */
  id: computed.readOnly('primitive.$id'),

  /**
    The unique identifier of the object.

    @property repositoryObject
    @type String
  */
  repositoryObject: computed.alias('primitive.RepositoryObject'),

  /**
    Returns the string representation of the primitive.

    @method toString
    @return {String} A string representation of the primitive.
  */
  toString() {
    return JSON.stringify(this.get('primitive'));
  },

  /**
    Returns the object to convert to the JSON.

    @method toJSON
    @return {Object} A object to convert to the JSON.
  */
  toJSON() {
    return this.get('primitive');
  },

  /**
    Returns an object for the JointJS diagram model.

    @method JointJS
    @return {joint.shapes} Object for the JointJS diagram model.
  */
  JointJS() {
    throw new Error('Not implemented.');
  },
});

joint.highlighters.strokeAndButtons = {
  _buttons: {},

  highlight: function(cellView, magnetEl, opt) {
    //joint.highlighters.stroke.highlight(...arguments);
    let stroke = joint.highlighters.stroke;
    let V = joint.Vectorizer;

    let id = stroke.getHighlighterId(magnetEl, opt);

    // Only highlight once.
    if (stroke._views[id]) {
      return;
    }

    let options = joint.util.defaults(opt || {}, stroke.defaultOptions);
    let magnetVel = V(magnetEl);
    let magnetBBox;
    let pathData;

    try
    {
      pathData = magnetVel.convertToPathData();
    }
    catch (error)
    {
      // Failed to get path data from magnet element.
      // Draw a rectangle around the entire cell view instead.
      magnetBBox = magnetVel.bbox(true/* without transforms */);
      pathData = V.rectToPath(joint.util.assign({}, options, magnetBBox));
    }

    let highlightVel = V('path').attr({
      'd': pathData,
      'pointer-events': 'none',
      'vector-effect': 'non-scaling-stroke',
      'fill': 'none'
    }).attr(options.attrs);

    let highlightMatrix = magnetVel.getTransformToElement(cellView.el);

    // Add padding to the highlight element.
    let padding = options.padding;
    if (padding) {
      magnetBBox || (magnetBBox = magnetVel.bbox(true));

      let cx = magnetBBox.x + (magnetBBox.width / 2);
      let cy = magnetBBox.y + (magnetBBox.height / 2);

      magnetBBox = V.transformRect(magnetBBox, highlightMatrix);

      let width = Math.max(magnetBBox.width, 1);
      let height = Math.max(magnetBBox.height, 1);
      let sx = (width + padding) / width;
      let sy = (height + padding) / height;
      let paddingMatrix = V.createSVGMatrix({
        a: sx,
        b: 0,
        c: 0,
        d: sy,
        e: cx - sx * cx,
        f: cy - sy * cy
      });

      highlightMatrix = highlightMatrix.multiply(paddingMatrix);
    }

    highlightVel.transform(highlightMatrix);

    // joint.mvc.View will handle the theme class name and joint class name prefix.
    let highlightView = stroke._views[id] = new joint.mvc.View({
      svgElement: true,
      className: 'highlight-stroke',
      el: highlightVel.node
    });

    // Remove the highlight view when the cell is removed from the graph.
    let removeHandler = function() {
      this.removeButtons(id);
      stroke.removeHighlighter.bind(stroke, id);
    }.bind(this);

    let cell = cellView.model;
    highlightView.listenTo(cell, 'remove', removeHandler);
    highlightView.listenTo(cell.graph, 'reset', removeHandler);

    cellView.vel.append(highlightVel);
    if (cellView.getButtons instanceof Function) {
      this.addButtons(cellView, id);
    }

    $(magnetEl.firstChild.children).attr({'fill-opacity':'1'});
    cellView.model.set('highlighted', true);
  },

  unhighlight: function(cellView, magnetEl, opt) {
    joint.highlighters.stroke.unhighlight(...arguments);
    let stroke = joint.highlighters.stroke;
    let id = stroke.getHighlighterId(magnetEl, opt);
    this.removeButtons(id);
    $(magnetEl.firstChild.children).attr({'fill-opacity':'0'});

    cellView.model.set('highlighted', false);
  },

  addButtons(cellView, id) {
    let buttons = cellView.getButtons();
    buttons.forEach(button => {
      let svgNS = 'http://www.w3.org/2000/svg';
      let g = document.createElementNS(svgNS, 'g');
      let name = get(button, 'name');
      g.setAttributeNS(null, 'class', name || '');
      let circle = document.createElementNS(svgNS, 'circle');
      let text = document.createElementNS(svgNS, 'text');
      text.innerHTML = get(button, 'text');
      g.appendChild(circle);
      g.appendChild(text);
      g.onclick = get(button, 'handler');
      if (!isArray(this._buttons[id])) {
        this._buttons[id] = A();
      }

      this._buttons[id].addObject(g);
      cellView.vel.append(g);
      cellView.model.attr(`.${name}`, get(button, 'attrs.element'));
      cellView.model.attr(`.${name}>circle`, get(button, 'attrs.circle'));
      cellView.model.attr(`.${name}>text`, get(button, 'attrs.text'));
    }, this);

    cellView.update();
  },

  removeButtons(id) {
    if (isArray(this._buttons[id])) {
      this._buttons[id].forEach(button => {
        button.remove();
      }, this);
    }

    this._buttons[id] = null;
  }
};

joint.shapes.basic.Generic.define('flexberry.uml.PrimitiveElementView');
joint.shapes.flexberry.uml.PrimitiveElementView = joint.dia.ElementView.extend({
  getButtons() {
    return A([{
      name: 'remove-button',
      text: '&#xf00d',
      handler: this.removeElement.bind(this),
      attrs: {
        'element': {'ref-dx': 0,'ref-y': 0, 'ref': '.joint-highlight-stroke' },
        'circle': { r: 6, fill: '#007aff', stroke: '#007aff', 'stroke-width': 1 },
        'text': { fill: '#ffffff','font-size': 10, 'text-anchor': 'middle', x: 0, y: 3, 'font-family': 'Icons' },
      }
    }]);
  },

  removeElement(e) {
    e.stopPropagation();
    this.model.remove();
  },
});
<commit_msg>Fix highlighting uml (#428)<commit_after>/**
  @module ember-flexberry-designer
*/

import EmberObject from '@ember/object';
import { computed, get } from '@ember/object';
import { A, isArray } from '@ember/array';
import joint from 'npm:jointjs';

/**
  An object that defines any primitive on the UML diagram.

  @class FdUmlPrimitive
  @extends Ember.Object
*/
export default EmberObject.extend({

  /**
    The object that describes the primitive.

    @property primitive
    @type Object
  */
  primitive: undefined,

  /**
    The unique identifier of the primitive.

    @property id
    @type String
  */
  id: computed.readOnly('primitive.$id'),

  /**
    The unique identifier of the object.

    @property repositoryObject
    @type String
  */
  repositoryObject: computed.alias('primitive.RepositoryObject'),

  /**
    Returns the string representation of the primitive.

    @method toString
    @return {String} A string representation of the primitive.
  */
  toString() {
    return JSON.stringify(this.get('primitive'));
  },

  /**
    Returns the object to convert to the JSON.

    @method toJSON
    @return {Object} A object to convert to the JSON.
  */
  toJSON() {
    return this.get('primitive');
  },

  /**
    Returns an object for the JointJS diagram model.

    @method JointJS
    @return {joint.shapes} Object for the JointJS diagram model.
  */
  JointJS() {
    throw new Error('Not implemented.');
  },
});

joint.highlighters.strokeAndButtons = {
  _buttons: {},

  highlight: function(cellView, magnetEl, opt) {
    //joint.highlighters.stroke.highlight(...arguments);
    let stroke = joint.highlighters.stroke;
    let V = joint.Vectorizer;

    let id = stroke.getHighlighterId(magnetEl, opt);

    // Only highlight once.
    if (stroke._views[id]) {
      return;
    }

    let options = joint.util.defaults(opt || {}, stroke.defaultOptions);
    let magnetVel = V(magnetEl);
    let magnetBBox;
    let pathData;

    try
    {
      pathData = magnetVel.convertToPathData();
    }
    catch (error)
    {
      // Failed to get path data from magnet element.
      // Draw a rectangle around the entire cell view instead.
      magnetBBox = magnetVel.bbox(true/* without transforms */);
      pathData = V.rectToPath(joint.util.assign({}, options, magnetBBox));
    }

    let highlightVel = V('path').attr({
      'd': pathData,
      'pointer-events': 'none',
      'vector-effect': 'non-scaling-stroke',
      'fill': 'none'
    }).attr(options.attrs);

    let highlightMatrix = magnetVel.getTransformToElement(cellView.el);

    // Add padding to the highlight element.
    let padding = options.padding;
    if (padding) {
      magnetBBox || (magnetBBox = magnetVel.bbox(true));

      let cx = magnetBBox.x + (magnetBBox.width / 2);
      let cy = magnetBBox.y + (magnetBBox.height / 2);

      magnetBBox = V.transformRect(magnetBBox, highlightMatrix);

      let width = Math.max(magnetBBox.width, 1);
      let height = Math.max(magnetBBox.height, 1);
      let sx = (width + padding) / width;
      let sy = (height + padding) / height;
      let paddingMatrix = V.createSVGMatrix({
        a: sx,
        b: 0,
        c: 0,
        d: sy,
        e: cx - sx * cx,
        f: cy - sy * cy
      });

      highlightMatrix = highlightMatrix.multiply(paddingMatrix);
    }

    highlightVel.transform(highlightMatrix);

    // joint.mvc.View will handle the theme class name and joint class name prefix.
    let highlightView = stroke._views[id] = new joint.mvc.View({
      svgElement: true,
      className: 'highlight-stroke',
      el: highlightVel.node
    });

    // Remove the highlight view when the cell is removed from the graph.
    let removeHandler = function() {
      this.removeButtons(id);
      stroke.removeHighlighter.bind(stroke, id);
    }.bind(this);

    let cell = cellView.model;
    highlightView.listenTo(cell, 'remove', removeHandler);
    highlightView.listenTo(cell.graph, 'reset', removeHandler);

    cellView.vel.append(highlightVel);
    if (cellView.getButtons instanceof Function) {
      this.addButtons(cellView, id);
    }

    /* Remove for fix highlighting more-class and template-class
     $(magnetEl.firstChild.children).attr({'fill-opacity':'1'});*/
    cellView.model.set('highlighted', true);
  },

  unhighlight: function(cellView, magnetEl, opt) {
    joint.highlighters.stroke.unhighlight(...arguments);
    let stroke = joint.highlighters.stroke;
    let id = stroke.getHighlighterId(magnetEl, opt);
    this.removeButtons(id);

    /* Remove for fix highlighting more-class and template-class
    $(magnetEl.firstChild.children).attr({'fill-opacity':'0'});*/

    cellView.model.set('highlighted', false);
  },

  addButtons(cellView, id) {
    let buttons = cellView.getButtons();
    buttons.forEach(button => {
      let svgNS = 'http://www.w3.org/2000/svg';
      let g = document.createElementNS(svgNS, 'g');
      let name = get(button, 'name');
      g.setAttributeNS(null, 'class', name || '');
      let circle = document.createElementNS(svgNS, 'circle');
      let text = document.createElementNS(svgNS, 'text');
      text.innerHTML = get(button, 'text');
      g.appendChild(circle);
      g.appendChild(text);
      g.onclick = get(button, 'handler');
      if (!isArray(this._buttons[id])) {
        this._buttons[id] = A();
      }

      this._buttons[id].addObject(g);
      cellView.vel.append(g);
      cellView.model.attr(`.${name}`, get(button, 'attrs.element'));
      cellView.model.attr(`.${name}>circle`, get(button, 'attrs.circle'));
      cellView.model.attr(`.${name}>text`, get(button, 'attrs.text'));
    }, this);

    cellView.update();
  },

  removeButtons(id) {
    if (isArray(this._buttons[id])) {
      this._buttons[id].forEach(button => {
        button.remove();
      }, this);
    }

    this._buttons[id] = null;
  }
};

joint.shapes.basic.Generic.define('flexberry.uml.PrimitiveElementView');
joint.shapes.flexberry.uml.PrimitiveElementView = joint.dia.ElementView.extend({
  getButtons() {
    return A([{
      name: 'remove-button',
      text: '&#xf00d',
      handler: this.removeElement.bind(this),
      attrs: {
        'element': {'ref-dx': 0,'ref-y': 0, 'ref': '.joint-highlight-stroke' },
        'circle': { r: 6, fill: '#007aff', stroke: '#007aff', 'stroke-width': 1 },
        'text': { fill: '#ffffff','font-size': 10, 'text-anchor': 'middle', x: 0, y: 3, 'font-family': 'Icons' },
      }
    }]);
  },

  removeElement(e) {
    e.stopPropagation();
    this.model.remove();
  },
});
<|endoftext|>


<commit_before>function format(oldFormat, newFormat, text) {
    if (oldFormat === 'standard-tab' && newFormat === 'wednesdays') {
        text = text.replace(//g, '')
        var lines = text.split('\n');
        var currentChordLine;
        var output = "";
        for (let i = 0; i < lines.length; i++) {
            var line = lines[i];
            if (isChordLine(line)) {
                currentChordLine = line;
            } else if (currentChordLine != null) {
                output += interleaveChords(currentChordLine, line) + '\n'
                currentChordLine = null;
            } else {
                output += line += '\n';
            }
        }
        return output;
    }
}

function isChordLine(line) {
    return line.match(/\s{5}/) || line.match(/^[A-G]b?#?m?(maj)?[1-9]?$/);
}

function interleaveChords(chords, lyrics) {
    var output = "";
    var alreadyUsedChordCharsCount = 0;
    for (let char = 0; char < lyrics.length; char++) {
        if (chords[char] && chords[char] != " " && alreadyUsedChordCharsCount === 0) {
            var chordString = "";
            for (let chordCharIndex = char; chordCharIndex < chords.length; chordCharIndex++) {
                if (chords[chordCharIndex] === " ") break;
                chordString += chords[chordCharIndex];
                alreadyUsedChordCharsCount++;
            }
            output += "(" + chordString + ")" + lyrics[char];
        } else {
            if (alreadyUsedChordCharsCount > 0) alreadyUsedChordCharsCount--;
            output += lyrics[char];
        }
    }
    return output;
}
<commit_msg>Adds a more reasonable chord-matching regex for guitar tab import, courtesy of @birdcolour.<commit_after>function format(oldFormat, newFormat, text) {
    if (oldFormat === 'standard-tab' && newFormat === 'wednesdays') {
        text = text.replace(//g, '')
        var lines = text.split('\n');
        var currentChordLine;
        var output = "";
        for (let i = 0; i < lines.length; i++) {
            var line = lines[i];
            if (isChordLine(line)) {
                currentChordLine = line;
            } else if (currentChordLine != null) {
                output += interleaveChords(currentChordLine, line) + '\n'
                currentChordLine = null;
            } else {
                output += line += '\n';
            }
        }
        return output;
    }
}

function isChordLine(line) {
    line = line.replace(/\s*/g, '\s')
    return line.match(/[ABCDEFG][b#]?(?:m|dim|maj)?[79]?(?:sus[24])?/);
}

function interleaveChords(chords, lyrics) {
    var output = "";
    var alreadyUsedChordCharsCount = 0;
    for (let char = 0; char < lyrics.length; char++) {
        if (chords[char] && chords[char] != " " && alreadyUsedChordCharsCount === 0) {
            var chordString = "";
            for (let chordCharIndex = char; chordCharIndex < chords.length; chordCharIndex++) {
                if (chords[chordCharIndex] === " ") break;
                chordString += chords[chordCharIndex];
                alreadyUsedChordCharsCount++;
            }
            output += "(" + chordString + ")" + lyrics[char];
        } else {
            if (alreadyUsedChordCharsCount > 0) alreadyUsedChordCharsCount--;
            output += lyrics[char];
        }
    }
    return output;
}
<|endoftext|>


<commit_before>import express from 'express'
import {uriPathInHTMLData} from 'xss-filters';

const router = express.Router()

// TODO: Move this into a database and let the admin of ferreiro.me to
// create more urls as they need on the go
const SHORT_URL_TO_URL = {
    perfomance: 'https://www.ferreiro.me/blog/speed-up-your-website-frontend-8-practical-tips',
    redis: 'https://www.slideshare.net/jgferreiro/redis-76234580?qid=4408d51d-497d-44b1-8a7a-b688dbd51f83&v=&b=',
    roundabout: 'https://speakerdeck.com/ferreiro/web-performance-jorge-ferreiro-twitter-london-js-roundabout',
    'roundabout-feedback': 'https://forms.gle/8uiSR2wz2VEs8qp47',
    dailyfocus: 'https://www.slideshare.net/jgferreiro/redis-76234580?qid=4408d51d-497d-44b1-8a7a-b688dbd51f83&v=&b='
}

const MOVED_PERMANETLY_STATUS_CODE = 301

// TODO: Move this into the API repository.
const getShortenedUrl = (permalink) => (
    new Promise((resolve, reject) => {
        const url = SHORT_URL_TO_URL[permalink]

        if (!url) {
            return reject('No URL found')
        }

        return resolve(url)
    })
)

router.get('/:permalink', async (req, res) => {
    // TODO: Get the permalink from the url.
    // TODO: Scape bad XSS guys from here.
    const permalink = uriPathInHTMLData(req.params.permalink)

    // TODO: Implement in the DB repository a read-only getter.
    getShortenedUrl(permalink)
        .then(url => res.redirect(MOVED_PERMANETLY_STATUS_CODE, url))
        .catch(error => res.render(error))
})

module.exports = router;<commit_msg>Added rahma go/ route<commit_after>import express from 'express'
import {uriPathInHTMLData} from 'xss-filters';

const router = express.Router()

// TODO: Move this into a database and let the admin of ferreiro.me to
// create more urls as they need on the go
const SHORT_URL_TO_URL = {
    dailyfocus: 'https://www.slideshare.net/jgferreiro/redis-76234580?qid=4408d51d-497d-44b1-8a7a-b688dbd51f83&v=&b=',
    perfomance: 'https://www.ferreiro.me/blog/speed-up-your-website-frontend-8-practical-tips',
    rahma: 'https://www.youtube.com/playlist?list=PLaN1b7vXPDt6f3jYSdFbVeeEixCxWSepj',
    redis: 'https://www.slideshare.net/jgferreiro/redis-76234580?qid=4408d51d-497d-44b1-8a7a-b688dbd51f83&v=&b=',
    roundabout: 'https://speakerdeck.com/ferreiro/web-performance-jorge-ferreiro-twitter-london-js-roundabout',
    'roundabout-feedback': 'https://forms.gle/8uiSR2wz2VEs8qp47'
}

const MOVED_PERMANETLY_STATUS_CODE = 301

// TODO: Move this into the API repository.
const getShortenedUrl = (permalink) => (
    new Promise((resolve, reject) => {
        const url = SHORT_URL_TO_URL[permalink]

        if (!url) {
            return reject('No URL found')
        }

        return resolve(url)
    })
)

router.get('/:permalink', async (req, res) => {
    // TODO: Get the permalink from the url.
    // TODO: Scape bad XSS guys from here.
    const permalink = uriPathInHTMLData(req.params.permalink)

    // TODO: Implement in the DB repository a read-only getter.
    getShortenedUrl(permalink)
        .then(url => res.redirect(MOVED_PERMANETLY_STATUS_CODE, url))
        .catch(error => res.render(error))
})

module.exports = router;
<|endoftext|>


<commit_before>var Backbone = require('backbone');
var _ = require('underscore');
var EditorHelpers = require('../editor-helpers-extend');
var template = require('./data-observatory-measurements.tpl');
var selectedItemTemplate = require('./data-observatory-measurement-item.tpl');
var DropdownDialogView = require('./measurements-dialog-view');
var PopupManager = require('../../../popup-manager');

var ENTER_KEY_CODE = 13;
var STATE = {
  idle: 'idle',
  loading: 'loading',
  fetching: 'fetching',
  fetched: 'fetched',
  error: 'error'
};

Backbone.Form.editors.DataObservatoryDropdown = Backbone.Form.editors.Base.extend({

  tagName: 'div',
  className: 'u-ellipsis Editor-formSelect',

  events: {
    'click .js-button': '_onButtonClick',
    'keydown .js-button': '_onButtonKeyDown',
    'focus .js-button': function () {
      this.trigger('focus', this);
    },
    'blur': function () {
      this.trigger('blur', this);
    }
  },

  options: {
    selectedItemTemplate: selectedItemTemplate
  },

  initialize: function (opts) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
    EditorHelpers.setOptions(this, opts);

    this.template = opts.template || template;
    this.dialogMode = this.options.dialogMode || 'nested';

    this.measurementsCollection = this.options.measurements;

    this._initBinds();

    this._dialogView = new DropdownDialogView({
      measurementsCollection: this.measurementsCollection
    });
  },

  render: function () {
    var isLoading = this._isLoading();
    var isEmpty = !this._hasItems();
    var isDisabled = !isEmpty ? this.options.disabled : true;
    var name = this.model.get(this.options.keyAttr);
    var item = this.measurementsCollection.getItem(name);
    var isNull = name === null;
    var label = isNull ? this.options.placeholder : (item ? item.getName() : false);
    var placeholder = this.options.placeholder;

    this.$el.html(
      this.template({
        label: label,
        keyAttr: this.options.keyAttr,
        placeholder: placeholder,
        isDisabled: isDisabled,
        isLoading: isLoading,
        isEmpty: isEmpty,
        isNull: isNull
      })
    );

    this._popupManager = new PopupManager(this.cid, this.$el, this._dialogView.$el);
    this._popupManager.append(this.dialogMode);

    if (item) {
      this._renderLicense(item);
    }

    return this;
  },

  _initBinds: function () {
    var hide = function () {
      this._dialogView.hide();
      this._popupManager.untrack();
    }.bind(this);

    this.applyESCBind(hide);
    this.applyClickOutsideBind(hide);

    this.listenTo(this.measurementsCollection, 'change:selected', this._onItemSelected);
  },

  _syncSelected: function () {
    var name = this.model.get(this.options.keyAttr);
    var item;

    if (name) {
      item = this.measurementsCollection.getItem(name);
      if (item && !item.get('selected')) {
        this.measurementsCollection.setSelected(name);
      }
    }
  },

  _hasItems: function () {
    var state = this.measurementsCollection.getState();
    return state === STATE.fetched && this.measurementsCollection.length > 0;
  },

  _isLoading: function () {
    var state = this.measurementsCollection.getState();
    return state === STATE.fetching;
  },

  _onItemSelected: function (mdl) {
    this.measurementsCollection.setSelected(mdl.getValue());
    this._dialogView.hide();
    this._popupManager.untrack();
    this._renderLicense(mdl);
    this._renderButton(mdl).focus();

    this.trigger('change', this);
  },

  _onButtonClick: function () {
    if (this._hasItems()) {
      this._dialogView.toggle();
      this._dialogView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
    }
  },

  _onButtonKeyDown: function (ev) {
    if (ev.which === ENTER_KEY_CODE) {
      ev.preventDefault();
      if (!this._dialogView.isVisible()) {
        ev.stopPropagation();
        this._onButtonClick();
      } else {
        this._popupManager.track();
      }
    }
  },

  validate: function () {
    var value = this.getValue();
    var validators = this.schema.validators;
    var getValidator = this.getValidator;

    if (!validators) return null;

    // Run through validators until an error is found
    var error = null;
    _.every(validators, function (validator) {
      error = getValidator(validator)(value, {});

      return !!error;
    });

    // Return error to be aggregated by list
    return error ? error : null; // eslint-disable-line
  },

  focus: function () {
    this.$('.js-button').focus();
  },

  blur: function () {
    this.$('.js-button').blur();
  },

  getValue: function () {
    var item = this.measurementsCollection.getSelectedItem();
    if (item) {
      return item.getValue();
    }
    return;
  },

  setValue: function (value) {
    var selectedModel = this.measurementsCollection.setSelected(value);
    if (selectedModel) {
      this._renderButton(selectedModel);
    }
    this.value = value;
  },

  _renderButton: function (mdl) {
    var button = this.$('.js-button');
    var label = mdl.getName();
    var $html = this.options.selectedItemTemplate({
      label: label
    });

    button
      .removeClass('is-empty')
      .attr('title', label)
      .html($html);

    return button;
  },

  _renderLicense: function (mdl) {
    var license = mdl.get('license');
    var $license = this.$('.js-license');
    $license
      .removeClass('u-isHidden')
      .find('span')
      .text(license);
  },

  remove: function () {
    this._popupManager && this._popupManager.destroy();
    this._dialogView && this._dialogView.clean();
    Backbone.Form.editors.Base.prototype.remove.call(this);
  }
});
<commit_msg>Delete useless code.<commit_after>var Backbone = require('backbone');
var _ = require('underscore');
var EditorHelpers = require('../editor-helpers-extend');
var template = require('./data-observatory-measurements.tpl');
var selectedItemTemplate = require('./data-observatory-measurement-item.tpl');
var DropdownDialogView = require('./measurements-dialog-view');
var PopupManager = require('../../../popup-manager');

var ENTER_KEY_CODE = 13;
var STATE = {
  idle: 'idle',
  loading: 'loading',
  fetching: 'fetching',
  fetched: 'fetched',
  error: 'error'
};

Backbone.Form.editors.DataObservatoryDropdown = Backbone.Form.editors.Base.extend({

  tagName: 'div',
  className: 'u-ellipsis Editor-formSelect',

  events: {
    'click .js-button': '_onButtonClick',
    'keydown .js-button': '_onButtonKeyDown',
    'focus .js-button': function () {
      this.trigger('focus', this);
    },
    'blur': function () {
      this.trigger('blur', this);
    }
  },

  options: {
    selectedItemTemplate: selectedItemTemplate
  },

  initialize: function (opts) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, opts);
    EditorHelpers.setOptions(this, opts);

    this.template = opts.template || template;
    this.dialogMode = this.options.dialogMode || 'nested';

    this.measurementsCollection = this.options.measurements;

    this._initBinds();

    this._dialogView = new DropdownDialogView({
      measurementsCollection: this.measurementsCollection
    });
  },

  render: function () {
    var isLoading = this._isLoading();
    var isEmpty = !this._hasItems();
    var isDisabled = !isEmpty ? this.options.disabled : true;
    var name = this.model.get(this.options.keyAttr);
    var item = this.measurementsCollection.getItem(name);
    var isNull = name === null;
    var label = isNull ? this.options.placeholder : (item ? item.getName() : false);
    var placeholder = this.options.placeholder;

    this.$el.html(
      this.template({
        label: label,
        keyAttr: this.options.keyAttr,
        placeholder: placeholder,
        isDisabled: isDisabled,
        isLoading: isLoading,
        isEmpty: isEmpty,
        isNull: isNull
      })
    );

    this._popupManager = new PopupManager(this.cid, this.$el, this._dialogView.$el);
    this._popupManager.append(this.dialogMode);

    if (item) {
      this._renderLicense(item);
    }

    return this;
  },

  _initBinds: function () {
    var hide = function () {
      this._dialogView.hide();
      this._popupManager.untrack();
    }.bind(this);

    this.applyESCBind(hide);
    this.applyClickOutsideBind(hide);

    this.listenTo(this.measurementsCollection, 'change:selected', this._onItemSelected);
  },

  _hasItems: function () {
    var state = this.measurementsCollection.getState();
    return state === STATE.fetched && this.measurementsCollection.length > 0;
  },

  _isLoading: function () {
    var state = this.measurementsCollection.getState();
    return state === STATE.fetching;
  },

  _onItemSelected: function (mdl) {
    this.measurementsCollection.setSelected(mdl.getValue());
    this._dialogView.hide();
    this._popupManager.untrack();
    this._renderLicense(mdl);
    this._renderButton(mdl).focus();

    this.trigger('change', this);
  },

  _onButtonClick: function () {
    if (this._hasItems()) {
      this._dialogView.toggle();
      this._dialogView.isVisible() ? this._popupManager.track() : this._popupManager.untrack();
    }
  },

  _onButtonKeyDown: function (ev) {
    if (ev.which === ENTER_KEY_CODE) {
      ev.preventDefault();
      if (!this._dialogView.isVisible()) {
        ev.stopPropagation();
        this._onButtonClick();
      } else {
        this._popupManager.track();
      }
    }
  },

  validate: function () {
    var value = this.getValue();
    var validators = this.schema.validators;
    var getValidator = this.getValidator;

    if (!validators) return null;

    // Run through validators until an error is found
    var error = null;
    _.every(validators, function (validator) {
      error = getValidator(validator)(value, {});

      return !!error;
    });

    // Return error to be aggregated by list
    return error ? error : null; // eslint-disable-line
  },

  focus: function () {
    this.$('.js-button').focus();
  },

  blur: function () {
    this.$('.js-button').blur();
  },

  getValue: function () {
    var item = this.measurementsCollection.getSelectedItem();
    if (item) {
      return item.getValue();
    }
    return;
  },

  setValue: function (value) {
    var selectedModel = this.measurementsCollection.setSelected(value);
    if (selectedModel) {
      this._renderButton(selectedModel);
    }
    this.value = value;
  },

  _renderButton: function (mdl) {
    var button = this.$('.js-button');
    var label = mdl.getName();
    var $html = this.options.selectedItemTemplate({
      label: label
    });

    button
      .removeClass('is-empty')
      .attr('title', label)
      .html($html);

    return button;
  },

  _renderLicense: function (mdl) {
    var license = mdl.get('license');
    var $license = this.$('.js-license');
    $license
      .removeClass('u-isHidden')
      .find('span')
      .text(license);
  },

  remove: function () {
    this._popupManager && this._popupManager.destroy();
    this._dialogView && this._dialogView.clean();
    Backbone.Form.editors.Base.prototype.remove.call(this);
  }
});
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Exercise = mongoose.model('Exercise');
var randGen = require('mongoose-query-random');
var shuffle = require('shuffle-array');

module.exports.getExercise = function (req, res) {

  if (!req.payload._id) {
    res.status(401).json({
      "message": "UnauthorizedError: private exercise"
    });
  } else {
    Exercise
      .find()
        .random(1, true, function (err, exercise) {
        console.log(exercise[0].badSolutions);
        shuffle(exercise[0].badSolutions);
        console.log(exercise[0].badSolutions);
        res.status(200).json(exercise[0]);
      });
  }

};

module.exports.getExercisesForSubjectAndSubSubject = function(req, res) {
  if (!req.payload._id) {
    res.status(401).json({
      "message": "UnauthorizedError: private exercise"
    });
  } else {
    var subject = new mongoose.mongo.ObjectId(req.query.subject);
    var subSubject = new mongoose.mongo.ObjectId(req.query.subSubject);
    Exercise
      .find({'subject' : subject, 'subSubject': subSubject})
      .exec(function(err, exercises) {
        res.status(200).json(exercises);
      });
  }

}

module.exports.newExercise = function (req, res) {

  // if(!req.body.name || !req.body.email || !req.body.password) {
  //   sendJSONresponse(res, 400, {
  //     "message": "All fields required"
  //   });
  //   return;
  // }
  console.log("in exercise.js newExercise with: " + req.body);

  var exercise = new Exercise();
  exercise.exercise = req.body.exercise;
  exercise.subject = req.body.subject;
  exercise.subSubject = req.body.subSubject;
  exercise.goodSolutions = req.body.solutions.slice(0, 1);
  exercise.badSolutions = req.body.solutions.slice(1);

  exercise.save(function (err) {
    res.status(200);
    res.json({
      "exercise": "saved"
    });
  });
};

<commit_msg>test commit<commit_after>var mongoose = require('mongoose');
var Exercise = mongoose.model('Exercise');
var randGen = require('mongoose-query-random');
var shuffle = require('shuffle-array');

module.exports.getExercise = function (req, res) {

  if (!req.payload._id) {
    res.status(401).json({
      "message": "UnauthorizedError: private exercise"
    });
  } else {
    Exercise
      .find()
        .random(1, true, function (err, exercise) {
        console.log(exercise[0].badSolutions);
        shuffle(exercise[0].badSolutions);
        console.log(exercise[0].badSolutions);
        res.status(200).json(exercise[0]);
      });
  }

};

module.exports.getExercisesForSubjectAndSubSubject = function(req, res) {
  if (!req.payload._id) {
    res.status(401).json({
      "message": "UnauthorizedError: private exercise"
    });
  } else {
    var subject = new mongoose.mongo.ObjectId(req.query.subject);
    var subSubject = new mongoose.mongo.ObjectId(req.query.subSubject);
    Exercise
      .find({'subject' : subject, 'subSubject': subSubject})
      .exec(function(err, exercises) {
        res.status(200).json(exercises);
      });
  }

}
//TODO this is just a git test
module.exports.getExercisesFoSubSubjectandLevel = function(req, res) {
  if (!req.payload._id) {
    res.status(401).json({
      "message": "UnauthorizedError: private exercise"
    });
  } else {
    var subject = new mongoose.mongo.ObjectId(req.query.subject);
    var subSubject = new mongoose.mongo.ObjectId(req.query.subSubject);
    Exercise
      .find({'subject' : subject, 'subSubject': subSubject})
      .exec(function(err, exercises) {
        res.status(200).json(exercises);
      });
  }

}

module.exports.newExercise = function (req, res) {

  // if(!req.body.name || !req.body.email || !req.body.password) {
  //   sendJSONresponse(res, 400, {
  //     "message": "All fields required"
  //   });
  //   return;
  // }
  console.log("in exercise.js newExercise with: " + req.body);

  var exercise = new Exercise();
  exercise.exercise = req.body.exercise;
  exercise.subject = req.body.subject;
  exercise.subSubject = req.body.subSubject;
  exercise.goodSolutions = req.body.solutions.slice(0, 1);
  exercise.badSolutions = req.body.solutions.slice(1);

  exercise.save(function (err) {
    res.status(200);
    res.json({
      "exercise": "saved"
    });
  });
};

<|endoftext|>


<commit_before>'use strict';

/**
 * nodecached client.
 * (C) 2013 Alex Fernández.
 */

// requires
var Log = require('log');
var net = require('net');
var testing = require('testing');

// constants
var DEFAULT_PORT = 11211;
var TIMEOUT = 5000;

// globals
var log = new Log('info');

/**
 * A client object.
 */
exports.Client = function(options, callback)
{
	// self-reference
	var self = this;

	// attributes
	var connection;
	var pending = null;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		options.port = options.port || DEFAULT_PORT;
		connection = net.connect(options);
		connection.setTimeout(TIMEOUT);
		connection.on('connect', function()
		{
			log.debug('Client connected to server');
			callback(null);
		});
		connection.on('end', function()
		{
			log.debug('Client disconnected from server');
			if (pending)
			{
				pending(null);
			}
		});
		connection.on('error', function(error)
		{
			log.error('Connection error: %s', error);
			if (callback)
			{
				callback(error);
			}
			connection.destroy();
		});
		connection.on('data', function(data)
		{
			if (!pending)
			{
				log.error('Unexpected data %s', data);
				return;
			}
			pending(data);
		});
		connection.on('timeout', function()
		{
			if (pending)
			{
				pending('timeout');
			}
		});
	}

	/**
	 * Get a value.
	 */
	self.get = function(key, callback)
	{
		pending = getGetter(callback);
		connection.write('get ' + key + '\r\n');
	};

	/**
	 * Get a function to receive the output of a get.
	 */
	function getGetter(callback)
	{
		return function(data)
		{
			callback(null, data);
		};
	}

	/**
	 * Set a value.
	 */
	self.set = function(key, exptime, value, callback)
	{
		pending = getSetter(callback);
		var message = value;
		if (typeof value != 'string')
		{
			message = JSON.stringify(value);
		}
		connection.write('set ' + key + ' 0 ' + exptime + ' ' + message.length + '\r\n');
		connection.write(message + '\r\n');
	};

	/**
	 * Get a function to receive the output of a set.
	 */
	function getSetter(callback)
	{
		return function(data)
		{
			callback(null, data);
		};
	}

	/**
	 * Delete a value.
	 */
	self.delete = function(key, callback)
	{
		pending = getDeleter(callback);
		connection.write('delete ' + key + '\r\n');
	};

	/**
	 * Get a function to receive the output of a delete.
	 */
	function getDeleter(callback)
	{
		return function(data)
		{
			callback(null, data);
		};
	}

	/**
	 * Stop the client.
	 */
	self.stop = function(callback)
	{
		log.debug('Stopping the client');
		pending = callback;
		connection.end();
	};
};

/**
 * Test get, set and delete.
 */
function testGetSetDelete(callback)
{
	var options = {
		port: 11215,
	};
	var key = 'test';
	var value = {
		a: 'b',
	};
	var client = new exports.Client(options, function(error)
	{
		testing.check(error, 'Could not create client', callback);
		client.set(key, 0, value, function(error, result)
		{
			testing.check(error, 'Could not set value', callback);
			log.info('set result: %s', result);
			client.get(key, function(error, result)
			{
				testing.check(error, 'Could not get value', callback);
				log.info('get result: %s', result);
				client.delete(key, function(error, result)
				{
					testing.check(error, 'Could not delete value', callback);
					log.info('delete result: %s', result);
					client.get(key, function(error, result)
					{
						testing.check(error, 'Could not get after delete', callback);
						log.info('get after delete result: %s', result);
						client.stop(callback);
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	log.debug('Running tests');
	testing.run({
		getSetDelete: testGetSetDelete,
	}, callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<commit_msg>Make get, set and delete work.<commit_after>'use strict';

/**
 * nodecached client.
 * (C) 2013 Alex Fernández.
 */

// requires
require('prototypes');
var Log = require('log');
var net = require('net');
var testing = require('testing');

// constants
var DEFAULT_PORT = 11211;
var TIMEOUT = 5000;

// globals
var log = new Log('info');

/**
 * A client object.
 */
exports.Client = function(options, callback)
{
	// self-reference
	var self = this;

	// attributes
	var connection;
	var pending = null;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		options.port = options.port || DEFAULT_PORT;
		connection = net.connect(options);
		connection.setTimeout(TIMEOUT);
		connection.on('connect', function()
		{
			log.debug('Client connected to server');
			callback(null);
		});
		connection.on('end', function()
		{
			log.debug('Client disconnected from server');
			if (pending)
			{
				pending(null);
			}
		});
		connection.on('error', function(error)
		{
			log.error('Connection error: %s', error);
			if (callback)
			{
				callback(error);
			}
			connection.destroy();
		});
		connection.on('data', function(data)
		{
			if (!pending)
			{
				log.error('Unexpected data %s', data);
				return;
			}
			pending(null, data);
		});
		connection.on('timeout', function()
		{
			if (pending)
			{
				pending('timeout');
			}
		});
	}

	/**
	 * Get a value.
	 */
	self.get = function(key, callback)
	{
		pending = getGetter(callback);
		connection.write('get ' + key + '\r\n');
	};

	/**
	 * Get a function to receive the output of a get.
	 */
	function getGetter(callback)
	{
		return getPending(callback, function(data)
		{
			var parsed = parseGet(data);
			if (!parsed)
			{
				return callback(null, parsed);
			}
			if (parsed.startsWith('{') && parsed.endsWith('}'))
			{
				try
				{
					return callback(null, JSON.parse(parsed));
				}
				catch(exception)
				{
					log.error('Fake json in %s: %s', parsed, exception);
				}
			}
			callback(null, parsed);
		});
	}

	/**
	 * Parse the result of a get.
	 */
	function parseGet(data)
	{
		var lines = String(data).split('\r\n');
		var line = lines.shift();
		if (line == 'END')
		{
			return null;
		}
		var words = line.split(' ');
		var word = words.shift();
		if (word != 'VALUE')
		{
			log.error('Unknown token %s', word);
			return null;
		}
		word = words.shift();
		word = words.shift();
		word = words.shift();
		if (!word)
		{
			log.error('Invalid length %s', word);
			return null;
		}
		var length = parseInt(word, 10);
		line = lines.shift();
		if (line.length != length)
		{
			log.error('Unexpected line length not %s in %s', length, line);
			return null;
		}
		return line;
	}

	/**
	 * Set a value.
	 */
	self.set = function(key, exptime, value, callback)
	{
		pending = getSetter(callback);
		var message = value;
		if (typeof value != 'string')
		{
			message = JSON.stringify(value);
		}
		connection.write('set ' + key + ' 0 ' + exptime + ' ' + message.length + '\r\n');
		connection.write(message + '\r\n');
	};

	/**
	 * Get a function to receive the output of a set.
	 */
	function getSetter(callback)
	{
		return getPending(callback, function(data)
		{
			var result = String(data).trim();
			if (result == 'STORED')
			{
				return callback(null, true);
			}
			log.error('Set result: %s', result);
			callback(null, false);
		});
	}

	/**
	 * Delete a value.
	 */
	self.delete = function(key, callback)
	{
		pending = getDeleter(callback);
		connection.write('delete ' + key + '\r\n');
	};

	/**
	 * Get a function to receive the output of a delete.
	 */
	function getDeleter(callback)
	{
		return getPending(callback, function(data)
		{
			var result = String(data).trim();
			if (result == 'DELETED')
			{
				return callback(null, true);
			}
			log.error('Delete result: %s', result);
			callback(null, false);
		});
	}

	/**
	 * Get a pending function with a callback and a handler for data.
	 */
	function getPending(callback, handler)
	{
		return function(error, result)
		{
			if (error)
			{
				return callback(error);
			}
			return handler(result);
		};
	}

	/**
	 * Stop the client.
	 */
	self.stop = function(callback)
	{
		log.debug('Stopping the client');
		pending = callback;
		connection.end();
	};
};

/**
 * Test get, set and delete.
 */
function testGetSetDelete(callback)
{
	var options = {
		port: 11215,
	};
	var key = 'test';
	var value = {
		a: 'b',
	};
	var client = new exports.Client(options, function(error)
	{
		testing.check(error, 'Could not create client', callback);
		client.set(key, 0, value, function(error, result)
		{
			testing.check(error, 'Could not set value', callback);
			testing.assertEquals(result, true, 'Invalid set result', callback);
			client.get(key, function(error, result)
			{
				testing.check(error, 'Could not get value', callback);
				testing.assertEquals(result, value, 'Invalid get result', callback);
				client.delete(key, function(error, result)
				{
					testing.check(error, 'Could not delete value', callback);
					testing.assertEquals(result, true, 'Invalid delete result', callback);
					client.get(key, function(error, result)
					{
						testing.check(error, 'Could not get after delete', callback);
						testing.assertEquals(result, null, 'Invalid get deleted result', callback);
						client.stop(callback);
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	log.debug('Running tests');
	testing.run({
		getSetDelete: testGetSetDelete,
	}, callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Card, message } from 'antd';
import ReactCountdownClock from 'react-countdown-clock';

export default class Timer extends Component {
    shouldComponentUpdate(newProps)
    {
        if(newProps.state.mobsters[0] !== this.props.state.mobsters[0]) {
            this.props = newProps;
            return true;
        }
        if(newProps.state.isSwitching !== this.props.state.isSwitching) {
            this.props = newProps;
            return true;
        }
        if(newProps.state.isPlaying !== this.props.state.isPlaying) {
            this.props = newProps;
            return true;
        }
        return false;
    }

    onPlay = (props) => {
        if(props.state.mobsters.length < 1) {
            message.info('Need to add some mobsters to start the mob.');
            return this.onReset();
        }
        this.props.actions.updateState({isPlaying: true, isSwitching: false});

    }

    onStart = () => {
        if(this.props.state.mobsters.length < 1) {
            message.info('Need to add some mobsters to start the mob.');
            return this.onReset();
        }
        this.props.actions.updateState({isPlaying: false, isSwitching: true});
    }

    onFinish = () => {
        this.onStart();
        this.props.actions.switchNext();
    }

    onReset = () => {
        this.props.actions.updateState({isPlaying: false, isSwitching: false});
    }

    render() {
    let timer = this.props.state.isPlaying && !this.props.state.isSwitching
                ? <ReactCountdownClock
                    style={styles.countdown}
                    seconds={this.props.state.setting.mobDuration * 60}
                     color="black"
                     alpha={0.9}
                     size={300}
                    onComplete={this.onFinish} />
                : this.props.state.isSwitching && !this.props.state.isPlaying
                ? <ReactCountdownClock
                     style={styles.countdown}
                     seconds={this.props.state.setting.waitDuration}
                     color="black"
                     alpha={0.9}
                     size={300}
                    onComplete={(e) => this.onPlay(this.props)} />
                : null;
    let backgroundColor = this.props.state.isPlaying
                ? 'green'
                : this.props.state.isSwitching
                ? 'yellow'
                : 'red';

    return (
    <Card style={{background:backgroundColor, height: '465px'}}>
            {!this.props.state.isPlaying && !this.props.state.isSwitching ? <button style={{
                backgroundColor: 'Transparent',
                backgroundRepeat:'no-repeat',
                border: 'none',
                cursor:'pointer',
                overflow: 'hidden',
                fontSize: 45,
                outline:'none',
                width: '300px',
                height:'74vh'}} onClick={(e) => this.onStart()}>Press to Start...</button> : null}
            {timer}
         </Card>
        );
    }
}
// when the timer revs up a version, add in the pausing.
//  paused={true}
//  pausedText='▐▐'

const styles = {
    countdown: {
        padding: 0,
        margin: 'auto',
        display: 'block',
        position: 'absolute',
        top: 0,
        bottom: 0,
        left: 0,
        right: 0
    }
}<commit_msg>fixed some styling<commit_after>import React, { Component } from 'react';
import { Card, message } from 'antd';
import ReactCountdownClock from 'react-countdown-clock';

export default class Timer extends Component {
    shouldComponentUpdate(newProps)
    {
        if(newProps.state.mobsters[0] !== this.props.state.mobsters[0]) {
            this.props = newProps;
            return true;
        }
        if(newProps.state.isSwitching !== this.props.state.isSwitching) {
            this.props = newProps;
            return true;
        }
        if(newProps.state.isPlaying !== this.props.state.isPlaying) {
            this.props = newProps;
            return true;
        }
        return false;
    }

    onPlay = (props) => {
        if(props.state.mobsters.length < 1) {
            message.info('Need to add some mobsters to start the mob.');
            return this.onReset();
        }
        this.props.actions.updateState({isPlaying: true, isSwitching: false});

    }

    onStart = () => {
        if(this.props.state.mobsters.length < 1) {
            message.info('Need to add some mobsters to start the mob.');
            return this.onReset();
        }
        this.props.actions.updateState({isPlaying: false, isSwitching: true});
    }

    onFinish = () => {
        this.onStart();
        this.props.actions.switchNext();
    }

    onReset = () => {
        this.props.actions.updateState({isPlaying: false, isSwitching: false});
    }

    render() {
    let timer = this.props.state.isPlaying && !this.props.state.isSwitching
                ? <ReactCountdownClock
                    seconds={this.props.state.setting.mobDuration * 60}
                     color="black"
                     alpha={0.9}
                     size={300}
                    onComplete={this.onFinish} />
                : this.props.state.isSwitching && !this.props.state.isPlaying
                ? <ReactCountdownClock
                     seconds={this.props.state.setting.waitDuration}
                     color="black"
                     alpha={0.9}
                     size={300}
                    onComplete={(e) => this.onPlay(this.props)} />
                : null;
    let backgroundColor = this.props.state.isPlaying
                ? 'green'
                : this.props.state.isSwitching
                ? 'yellow'
                : 'red';

    return (
    <Card style={{background:backgroundColor, height: '465px', display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
            {!this.props.state.isPlaying && !this.props.state.isSwitching ? <button style={{
                backgroundColor: 'Transparent',
                backgroundRepeat:'no-repeat',
                border: 'none',
                cursor:'pointer',
                overflow: 'hidden',
                fontSize: 45,
                outline:'none',
                width: '300px',
                height:'74vh'}} onClick={(e) => this.onStart()}>Press to Start...</button> : null}
            {timer}
         </Card>
        );
    }
}
<|endoftext|>


<commit_before>/*
 * grunt-clear
 * https://github.com/geddesign/clear-cli
 *
 * Copyright (c) 2012 Dave Geddes
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
  // Please see the grunt documentation for more information regarding task
  // creation: https://github.com/cowboy/grunt/blob/master/docs/toc.md
  grunt.registerTask('clear', 'Clear your terminal window', function() {
    grunt.util.spawn({
      cmd: 'clear',
      args: null,
      fallback: ''
    }, function(error, result, code){
      grunt.log.write('should have cleared');
      done();
    });

    // var myTerminal = require("child_process").exec,
    //   commandToBeExecuted = "clear";

    // myTerminal(commandToBeExecuted, function(error, stdout, stderr) {
    //   grunt.log.write('should have cleared');
    //   if (!error) {
    //     grunt.log.write('cleared ' + this.name + ' task...').ok();
    //   }
    //   else{
    //     grunt.log.write('failed! ').error();
    //   }
    // });

    // console.log("\033[2J\033[0f");

  });
};<commit_msg>appears to be working now<commit_after>/*
 * grunt-clear
 * https://github.com/geddesign/grunt-clear
 *
 * Copyright (c) 2012 Dave Geddes
 * Licensed under the MIT license.
 */
'use strict';

module.exports = function(grunt) {
  grunt.registerTask('clear', 'Clear your terminal window', function() {
    var child = require('child_process');
    var ps = child.spawn('clear');
    ps.stdout.pipe(process.stdout);
    ps.stdin.end();
  });
};<|endoftext|>


<commit_before>import { InjectedProperty } from 'ember-metal';
import { assert } from 'ember-debug';

/**
  Namespace for injection helper methods.

  @class inject
  @namespace Ember
  @static
  @public
*/
export default function inject() {
  const helpers = Object.keys(inject)
    .map(k => `'inject.${k}'`)
    .join(' or ');
  assert(`Injected properties must be created through helpers, see '${helpers}'`);
}

// Dictionary of injection validations by type, added to by `createInjectionHelper`
const typeValidators = {};

/**
  This method allows other Ember modules to register injection helpers for a
  given container type. Helpers are exported to the `inject` namespace as the
  container type itself.

  @private
  @method createInjectionHelper
  @since 1.10.0
  @for Ember
  @param {String} type The container type the helper will inject
  @param {Function} validator A validation callback that is executed at mixin-time
*/
export function createInjectionHelper(type, validator) {
  typeValidators[type] = validator;

  inject[type] = name => new InjectedProperty(type, name);
}

/**
  Validation function that runs per-type validation functions once for each
  injected type encountered.

  @private
  @method validatePropertyInjections
  @since 1.10.0
  @for Ember
  @param {Object} factory The factory object
*/
export function validatePropertyInjections(factory) {
  let proto = factory.proto();
  let types = [];

  for (let key in proto) {
    let desc = proto[key];
    if (desc instanceof InjectedProperty && types.indexOf(desc.type) === -1) {
      types.push(desc.type);
    }
  }

  if (types.length) {
    for (let i = 0; i < types.length; i++) {
      let validator = typeValidators[types[i]];

      if (typeof validator === 'function') {
        validator(factory);
      }
    }
  }

  return true;
}
<commit_msg>Fix lint, assert behavior in inject assertion.<commit_after>import { InjectedProperty } from 'ember-metal';
import { assert } from 'ember-debug';

/**
  Namespace for injection helper methods.

  @class inject
  @namespace Ember
  @static
  @public
*/
export default function inject() {
  assert(`Injected properties must be created through helpers, see '${Object.keys(inject)
    .map(k => `'inject.${k}'`)
    .join(' or ')}'`);
}

// Dictionary of injection validations by type, added to by `createInjectionHelper`
const typeValidators = {};

/**
  This method allows other Ember modules to register injection helpers for a
  given container type. Helpers are exported to the `inject` namespace as the
  container type itself.

  @private
  @method createInjectionHelper
  @since 1.10.0
  @for Ember
  @param {String} type The container type the helper will inject
  @param {Function} validator A validation callback that is executed at mixin-time
*/
export function createInjectionHelper(type, validator) {
  typeValidators[type] = validator;

  inject[type] = name => new InjectedProperty(type, name);
}

/**
  Validation function that runs per-type validation functions once for each
  injected type encountered.

  @private
  @method validatePropertyInjections
  @since 1.10.0
  @for Ember
  @param {Object} factory The factory object
*/
export function validatePropertyInjections(factory) {
  let proto = factory.proto();
  let types = [];

  for (let key in proto) {
    let desc = proto[key];
    if (desc instanceof InjectedProperty && types.indexOf(desc.type) === -1) {
      types.push(desc.type);
    }
  }

  if (types.length) {
    for (let i = 0; i < types.length; i++) {
      let validator = typeValidators[types[i]];

      if (typeof validator === 'function') {
        validator(factory);
      }
    }
  }

  return true;
}
<|endoftext|>


<commit_before>var server = require('./lib/server');
var serverMessaging = require('./lib/server-messaging')(server);
var makeGulpSrcWrapper = require('./lib/gulp-src-wrapper');

module.exports = launch;

function launch(gulp) {
  var gulpSrcWrapper = makeGulpSrcWrapper(gulp);

  serverMessaging.on('client-ready', function() {
    var allFiles = '**';

    gulpSrcWrapper.run(allFiles, function(error, files) {
      serverMessaging.allFiles(files);
    });
  });

  serverMessaging.on('patterns', function(patterns) {
    gulpSrcWrapper.run(patterns, function(error, files) {
      serverMessaging.selectedFiles(files);
    });
  });

  var port = 3333;
  server.listen(port, function() {
    console.log('Server listening on port ' + port);
  });
}<commit_msg>fix requires<commit_after>var server = require('./server');
var serverMessaging = require('./server-messaging')(server);
var makeGulpSrcWrapper = require('./gulp-src-wrapper');

module.exports = launch;

function launch(gulp) {
  var gulpSrcWrapper = makeGulpSrcWrapper(gulp);

  serverMessaging.on('client-ready', function() {
    var allFiles = '**';

    gulpSrcWrapper.run(allFiles, function(error, files) {
      serverMessaging.allFiles(files);
    });
  });

  serverMessaging.on('patterns', function(patterns) {
    gulpSrcWrapper.run(patterns, function(error, files) {
      serverMessaging.selectedFiles(files);
    });
  });

  var port = 3333;
  server.listen(port, function() {
    console.log('Server listening on port ' + port);
  });
}<|endoftext|>