<commit_before>'use strict';
const patternEngines = require('./pattern_engines');
const path = require('path');
const extend = require('util')._extend;

// patternPrefixMatcher is intended to match the leading maybe-underscore,
// zero or more digits, and maybe-dash at the beginning of a pattern file name we can hack them
// off and get at the good part.
const patternPrefixMatcher = /^_?(\d+-)?/;

// Pattern properties
/**
 * Pattern constructor
 * @constructor
 */
const Pattern = function (relPath, data, patternlab) {
  /**
  * We expect relPath to be the path of the pattern template, relative to the
  * root of the pattern tree. Parse out the path parts and save the useful ones.
  * @param {relPath} relative directory
  * @param {data} The JSON used to render values in the pattern.
  * @param {patternlab} rendered html files for the pattern
  */
  const pathObj = path.parse(path.normalize(relPath));
  this.relPath = path.normalize(relPath); // '00-atoms/00-global/00-colors.mustache'
  this.fileName = pathObj.name;     // '00-colors'
  this.subdir = pathObj.dir;        // '00-atoms/00-global'
  this.fileExtension = pathObj.ext; // '.mustache'

  // this is the unique name, subDir + fileName (sans extension)
  this.name = this.subdir.replace(path.sep, '-') + '-' + this.fileName.replace('~', '-'); // '00-atoms-00-global-00-colors'

  // the JSON used to render values in the pattern
  this.jsonFileData = data || {};

  // strip leading "00-" from the file name and flip tildes to dashes
  this.patternBaseName = this.fileName.replace(patternPrefixMatcher, '').replace('~', '-'); // 'colors'

  // Fancy name. No idea how this works. 'Colors'
  this.patternName = this.patternBaseName.split('-').reduce(function (val, working) {
    return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1);
  }, '').trim(); //this is the display name for the ui. strip numeric + hyphen prefixes

  // the top-level pattern group this pattern belongs to. 'atoms'
  this.patternGroup = this.subdir.split(path.sep)[0].replace(patternPrefixMatcher, '');

  //00-atoms if needed
  this.patternType = this.subdir.split(path.sep)[0];

  // the sub-group this pattern belongs to.
  this.patternSubGroup = path.basename(this.subdir).replace(patternPrefixMatcher, ''); // 'global'

  //00-colors if needed
  this.patternSubType = path.basename(this.subdir);

  // the joined pattern group and subgroup directory
  this.flatPatternPath = this.subdir.replace(/[\/\\]/g, '-'); // '00-atoms-00-global'

  // calculated path from the root of the public directory to the generated
  // (rendered!) html file for this pattern, to be shown in the iframe
  this.patternLink = patternlab ? this.getPatternLink(patternlab, 'rendered') : null;

  // The canonical "key" by which this pattern is known. This is the callable
  // name of the pattern. UPDATE: this.key is now known as this.patternPartial
  this.patternPartial = this.patternGroup + '-' + this.patternBaseName;

  // Let's calculate the verbose name ahead of time! We don't use path.sep here
  // on purpose. This isn't a file name!
  this.verbosePartial = this.subdir.split(path.sep).join('/') + '/' + this.fileName;

  this.isPattern = true;
  this.isFlatPattern = this.patternGroup === this.patternSubGroup;
  this.patternState = '';
  this.template = '';
  this.patternPartialCode = '';
  this.lineage = [];
  this.lineageIndex = [];
  this.lineageR = [];
  this.lineageRIndex = [];
  this.isPseudoPattern = false;
  this.order = Number.MAX_SAFE_INTEGER;
  this.engine = patternEngines.getEngineForPattern(this);

  /**
   * Determines if this pattern needs to be recompiled.
   *
   * @ee {@link CompileState}*/
  this.compileState = null;

  /**
   * Timestamp in milliseconds when the pattern template or auxilary file (e.g. json) were modified.
   * If multiple files are affected, this is the timestamp of the most recent change.
   *
   * @see {@link pattern}
   */
  this.lastModified = null;

};

// Pattern methods

Pattern.prototype = {

  renderSync: function (data, partials) {
    console.log('renderSync');
    if (this.engine && !this.engine.isAsync) {
      const results = this.engine.renderPattern(this, data || this.jsonFileData, partials);
      return results;

    }
    return null;
  },

  // render function - acts as a proxy for the PatternEngine's
  render: function (data, partials) {
    console.log('renderAsync');
    //console.log(this);

    if (this.engine && this.engine.isAsync) {
      const promise = this.engine.renderPattern(this, data || this.jsonFileData, partials);
      return promise.then(results => {
        console.log(120, results);
        return results;
      }).catch((reason) => {
        console.log(123, reason);
        return Promise.reject(reason);
      });
    } else {
      // renderSync
      return Promise.resolve(
        this.renderSync(data, partials)
      );
    }
  },

  registerPartial: function () {
    if (this.engine && typeof this.engine.registerPartial === 'function') {
      this.engine.registerPartial(this);
    }
  },

  // calculated path from the root of the public directory to the generated html
  // file for this pattern.
  // Should look something like '00-atoms-00-global-00-colors/00-atoms-00-global-00-colors.html'
  getPatternLink: function (patternlab, suffixType, customfileExtension) {
    // if no suffixType is provided, we default to rendered
    const suffixConfig = patternlab.config.outputFileSuffixes;
    const suffix = suffixType ? suffixConfig[suffixType] : suffixConfig.rendered;

    if (suffixType === 'rawTemplate') {
      return this.name + path.sep + this.name + suffix + this.fileExtension;
    }

    if (suffixType === 'custom') {
      return this.name + path.sep + this.name + customfileExtension;
    }

    return this.name + path.sep + this.name + suffix + '.html';
  },

  // the finders all delegate to the PatternEngine, which also encapsulates all
  // appropriate regexes
  findPartials: function () {
    return this.engine.findPartials(this);
  },

  findPartialsWithStyleModifiers: function () {
    return this.engine.findPartialsWithStyleModifiers(this);
  },

  findPartialsWithPatternParameters: function () {
    return this.engine.findPartialsWithPatternParameters(this);
  },

  findListItems: function () {
    return this.engine.findListItems(this);
  },

  findPartial: function (partialString) {
    return this.engine.findPartial(partialString);
  }
};

// Pattern static methods

// factory: creates an empty Pattern for miscellaneous internal use, such as
// by list_item_hunter
Pattern.createEmpty = function (customProps, patternlab) {
  let relPath = '';
  if (customProps) {
    if (customProps.relPath) {
      relPath = customProps.relPath;
    } else if (customProps.subdir && customProps.filename) {
      relPath = customProps.subdir + path.sep + customProps.filename;
    }
  }

  const pattern = new Pattern(relPath, null, patternlab);
  return extend(pattern, customProps);
};

// factory: creates an Pattern object on-demand from a hash; the hash accepts
// parameters that replace the positional parameters that the Pattern
// constructor takes.
Pattern.create = function (relPath, data, customProps, patternlab) {
  const newPattern = new Pattern(relPath || '', data || null, patternlab);
  return extend(newPattern, customProps);
};

const CompileState = {
  NEEDS_REBUILD: "needs rebuild",
  BUILDING: "building",
  CLEAN: "clean"
};

module.exports = {
  Pattern: Pattern,
  CompileState: CompileState
};
<commit_msg>chore(object_factory.js): Remove debugging<commit_after>'use strict';
const patternEngines = require('./pattern_engines');
const path = require('path');
const extend = require('util')._extend;

// patternPrefixMatcher is intended to match the leading maybe-underscore,
// zero or more digits, and maybe-dash at the beginning of a pattern file name we can hack them
// off and get at the good part.
const patternPrefixMatcher = /^_?(\d+-)?/;

// Pattern properties
/**
 * Pattern constructor
 * @constructor
 */
const Pattern = function (relPath, data, patternlab) {
  /**
  * We expect relPath to be the path of the pattern template, relative to the
  * root of the pattern tree. Parse out the path parts and save the useful ones.
  * @param {relPath} relative directory
  * @param {data} The JSON used to render values in the pattern.
  * @param {patternlab} rendered html files for the pattern
  */
  const pathObj = path.parse(path.normalize(relPath));
  this.relPath = path.normalize(relPath); // '00-atoms/00-global/00-colors.mustache'
  this.fileName = pathObj.name;     // '00-colors'
  this.subdir = pathObj.dir;        // '00-atoms/00-global'
  this.fileExtension = pathObj.ext; // '.mustache'

  // this is the unique name, subDir + fileName (sans extension)
  this.name = this.subdir.replace(path.sep, '-') + '-' + this.fileName.replace('~', '-'); // '00-atoms-00-global-00-colors'

  // the JSON used to render values in the pattern
  this.jsonFileData = data || {};

  // strip leading "00-" from the file name and flip tildes to dashes
  this.patternBaseName = this.fileName.replace(patternPrefixMatcher, '').replace('~', '-'); // 'colors'

  // Fancy name. No idea how this works. 'Colors'
  this.patternName = this.patternBaseName.split('-').reduce(function (val, working) {
    return val.charAt(0).toUpperCase() + val.slice(1) + ' ' + working.charAt(0).toUpperCase() + working.slice(1);
  }, '').trim(); //this is the display name for the ui. strip numeric + hyphen prefixes

  // the top-level pattern group this pattern belongs to. 'atoms'
  this.patternGroup = this.subdir.split(path.sep)[0].replace(patternPrefixMatcher, '');

  //00-atoms if needed
  this.patternType = this.subdir.split(path.sep)[0];

  // the sub-group this pattern belongs to.
  this.patternSubGroup = path.basename(this.subdir).replace(patternPrefixMatcher, ''); // 'global'

  //00-colors if needed
  this.patternSubType = path.basename(this.subdir);

  // the joined pattern group and subgroup directory
  this.flatPatternPath = this.subdir.replace(/[\/\\]/g, '-'); // '00-atoms-00-global'

  // calculated path from the root of the public directory to the generated
  // (rendered!) html file for this pattern, to be shown in the iframe
  this.patternLink = patternlab ? this.getPatternLink(patternlab, 'rendered') : null;

  // The canonical "key" by which this pattern is known. This is the callable
  // name of the pattern. UPDATE: this.key is now known as this.patternPartial
  this.patternPartial = this.patternGroup + '-' + this.patternBaseName;

  // Let's calculate the verbose name ahead of time! We don't use path.sep here
  // on purpose. This isn't a file name!
  this.verbosePartial = this.subdir.split(path.sep).join('/') + '/' + this.fileName;

  this.isPattern = true;
  this.isFlatPattern = this.patternGroup === this.patternSubGroup;
  this.patternState = '';
  this.template = '';
  this.patternPartialCode = '';
  this.lineage = [];
  this.lineageIndex = [];
  this.lineageR = [];
  this.lineageRIndex = [];
  this.isPseudoPattern = false;
  this.order = Number.MAX_SAFE_INTEGER;
  this.engine = patternEngines.getEngineForPattern(this);

  /**
   * Determines if this pattern needs to be recompiled.
   *
   * @ee {@link CompileState}*/
  this.compileState = null;

  /**
   * Timestamp in milliseconds when the pattern template or auxilary file (e.g. json) were modified.
   * If multiple files are affected, this is the timestamp of the most recent change.
   *
   * @see {@link pattern}
   */
  this.lastModified = null;

};

// Pattern methods

Pattern.prototype = {

  renderSync: function (data, partials) {
    if (this.engine && !this.engine.isAsync) {
      const results = this.engine.renderPattern(this, data || this.jsonFileData, partials);
      return results;

    }
    return null;
  },

  // render function - acts as a proxy for the PatternEngine's
  render: function (data, partials) {
    if (this.engine && this.engine.isAsync) {
      const promise = this.engine.renderPattern(this, data || this.jsonFileData, partials);
      return promise.then(results => {
        return results;
      }).catch((reason) => {
        return Promise.reject(reason);
      });
    } else {
      // renderSync
      return Promise.resolve(
        this.renderSync(data, partials)
      );
    }
  },

  registerPartial: function () {
    if (this.engine && typeof this.engine.registerPartial === 'function') {
      this.engine.registerPartial(this);
    }
  },

  // calculated path from the root of the public directory to the generated html
  // file for this pattern.
  // Should look something like '00-atoms-00-global-00-colors/00-atoms-00-global-00-colors.html'
  getPatternLink: function (patternlab, suffixType, customfileExtension) {
    // if no suffixType is provided, we default to rendered
    const suffixConfig = patternlab.config.outputFileSuffixes;
    const suffix = suffixType ? suffixConfig[suffixType] : suffixConfig.rendered;

    if (suffixType === 'rawTemplate') {
      return this.name + path.sep + this.name + suffix + this.fileExtension;
    }

    if (suffixType === 'custom') {
      return this.name + path.sep + this.name + customfileExtension;
    }

    return this.name + path.sep + this.name + suffix + '.html';
  },

  // the finders all delegate to the PatternEngine, which also encapsulates all
  // appropriate regexes
  findPartials: function () {
    return this.engine.findPartials(this);
  },

  findPartialsWithStyleModifiers: function () {
    return this.engine.findPartialsWithStyleModifiers(this);
  },

  findPartialsWithPatternParameters: function () {
    return this.engine.findPartialsWithPatternParameters(this);
  },

  findListItems: function () {
    return this.engine.findListItems(this);
  },

  findPartial: function (partialString) {
    return this.engine.findPartial(partialString);
  }
};

// Pattern static methods

// factory: creates an empty Pattern for miscellaneous internal use, such as
// by list_item_hunter
Pattern.createEmpty = function (customProps, patternlab) {
  let relPath = '';
  if (customProps) {
    if (customProps.relPath) {
      relPath = customProps.relPath;
    } else if (customProps.subdir && customProps.filename) {
      relPath = customProps.subdir + path.sep + customProps.filename;
    }
  }

  const pattern = new Pattern(relPath, null, patternlab);
  return extend(pattern, customProps);
};

// factory: creates an Pattern object on-demand from a hash; the hash accepts
// parameters that replace the positional parameters that the Pattern
// constructor takes.
Pattern.create = function (relPath, data, customProps, patternlab) {
  const newPattern = new Pattern(relPath || '', data || null, patternlab);
  return extend(newPattern, customProps);
};

const CompileState = {
  NEEDS_REBUILD: "needs rebuild",
  BUILDING: "building",
  CLEAN: "clean"
};

module.exports = {
  Pattern: Pattern,
  CompileState: CompileState
};
<|endoftext|>


<commit_before><commit_msg>removed extract patient partial... unused<commit_after><|endoftext|>


<commit_before>// Document.Store (web debug)
// -----------------

var StaticDocStore = function() {

};

StaticDocStore.prototype.get = function(id, cb) {
  // HACK: under webkitgtk XMLHttpRequests do not work for local resources :(
  //       workaround is to add a native extension that loads local data
  //       usage: substance.resources.getJSON(url)  - returns JS object
  //          or  substance.resources.get(url)      - returns content as String
  if (typeof substance !== "undefined" &&
    typeof substance.resources !== "undefined") {
     doc = substance.resources.getJSON('data/'+ (id+'.json'));
     cb(null, doc);
  } else {
    $.getJSON('data/'+ (id+'.json'), function(doc) {
      cb(null, doc);
    });
  }
};

StaticDocStore.prototype.list = function(cb) {
  cb(null, [
    { properties: { title: "Substance" }, id: "substance" },
    { properties: { title: "Hello World" }, id: "hello" }
  ]);
};

StaticDocStore.prototype.create = function(cb) {
  cb(null, {id: Math.uuid()});
};

StaticDocStore.prototype.update = function(id, operations, cb) {
  // Implement
};

StaticDocStore.prototype.delete = function(id, cb) {
  // Implement
};

// Initialize DocStore
// -----------------

var store;

// store = new StaticDocStore();

if (window.redis) {
  store = new redis.RedisDocStore();
} else {
  store = new StaticDocStore();
}

// Update doc (docstore.update)
// -----------------

function updateDoc(doc, commit, cb) {
  store.update(doc.id, [commit], function(err) {
    store.setSnapshot(doc.id, doc.content, 'master', function(err) {
      // Update metadata accordingly
      if (commit.op[0] === "set") {
        _.extend(doc.meta, doc.content.properties);
        updateMeta(doc, cb);
      };
    });
  });
};

function updateMeta(doc, cb) {
  doc.meta.updated_at = new Date();
  store.updateMeta(doc.id, doc.meta, cb);
}

// Comments
// -----------------
// Called by Substance.Text

var Comments = function(session) {
  this.session = session;
  this.scopes = [];
};

_.extend(Comments.prototype, _.Events, {
  compute: function(scope) {
    var node = this.session.node();
    this.scopes = [];

    if (node) {
      var content = this.session.document.content.nodes[node].content;
      var annotations = this.session.document.annotations(node);
    }

    this.commentsForNode(this.session.document, node, content, annotations, scope);
  },

  // Based on a new set of annotations (during editing)
  updateAnnotations: function(content, annotations) {
    var node = this.session.node();

    // Only consider markers as comment scopes
    var annotations = _.filter(annotations, function(a) {
      return _.include(["idea", "blur", "doubt"], a.type);
    });

    this.commentsForNode(this.session.document, node, content, annotations);
  },

  commentsForNode: function(document, node, content, annotations, scope) {
    this.scopes = [];

    // Extract annotation text from the model
    function annotationText(a) {
      if (!a.pos) return "No pos";
      return content.substr(a.pos[0], a.pos[1]);
    }

    if (node) {
      this.scopes.push({
        name: "Node",
        type: "node",
        id: "node_comments",
        comments: document.comments(node)
      });

      _.each(annotations, function(a) {
        this.scopes.push({
          name: annotationText(a),
          type: a.type,
          annotation: a.id,
          id: a.id,
          comments: document.commentsForAnnotation(a.id)
        });
      }, this);
    } else {
      this.scopes.push({
        id: "document_comments",
        name: "Document",
        type: "document",
        comments: document.documentComments()
      });
    }

    this.session.trigger('comments:updated', scope);
  }
});

// Substance.Session
// -----------------
//
// The Composer works with a session object, which maintains
// all the state of a document session
// TODO: No multiuser support yet, use app.user

Substance.Session = function(options) {
  this.document = new Substance.Document(options);

  this.users = {
    "michael": {
      "color": "#2F2B26",
      "selection": []
    }
  };

  this.selections = {};

  // Comments view
  this.comments = new Comments(this);

  // That's the current editor
  this.user = "michael";
};

_.extend(Substance.Session.prototype, _.Events, {
  select: function(nodes, options) {
    if (!options) options = {};
    var user = options.user || this.user; // Use current user by default

    // Do nothing if selection hasn't changed
    if (!this.selectionChanged(user, nodes, !!options.edit)) return;

    this.edit = !!options.edit;

    if (this.users[this.user].selection) {
      _.each(this.users[user].selection, function(node) {
        delete this.selections[node];
      }, this);
    }

    this.users[user].selection = nodes;
    _.each(nodes, function(node) {
      this.selections[node] = user;
    }, this);

    // New selection leads to new comment context
    this.comments.compute();
    this.trigger('node:selected');
  },

  // Publish/Republish document
  publish: function(cb) {
    var doc = this.document;
    doc.meta.published_at = new Date();
    doc.meta.published_commit = doc.getRef('master');

    // TODO: create publication
    createPublication(doc, function(err) {
      console.log('created publication');
      if (err) alert('Failed to create a publication.');
      updateMeta(doc, cb);
    });
  },

  // Unpublish document
  unpublish: function(cb) {
    var doc = this.document;
    delete doc.meta["published_at"];
    delete doc.meta["published_commit"];

    clearPublications(doc, function(err) {
      console.log('cleared publications');
      // TODO: remove all publications
      updateMeta(doc, cb);
    });
  },

  publishState: function() {
    var doc = this.document;
    if (!doc.meta.published_commit) return "unpublished";
    if (doc.getRef('master') === doc.meta.published_commit) return "published";
    return "dirty";
  },

  // Checks if selection has actually changed for a user
  selectionChanged: function(user, nodes, edit) {
    return !_.isEqual(nodes, this.selection(user)) || edit !== this.edit;
  },

  // Retrieve current node selection
  selection: function(user) {
    if (!user) user = this.user;
    return this.users[user].selection;
  },

  // Returns the node id of current active node
  // Only works if there's just one node selected
  node: function() {
    var lvl = this.level(),
        sel = this.selection();

    if (lvl >= 2 && sel.length === 1) {
      return sel[0];
    }
  },

  // Returns current navigation level (1..3)
  level: function() {
    var selection = this.users[this.user].selection;

    // Edit mode
    if (this.edit) return 3;

    // Selection mode (one or more nodes)
    if (selection.length >= 1) return 2;

    // no selection -> document level
    return 1;
  }
});

// Load a document
// -----------------

function loadDocument(id, cb) {
  store.get(id, function(err, doc) {
    cb(err, new Substance.Session(doc));
  });
}

// List all documents
// -----------------

function listDocuments(cb) {
  store.list(function(err, documents) {
    console.log('store.list: ', documents);
    var res = _.map(documents, function(doc) {
      return {
        title: doc.meta.title,
        author: "le_author",
        file: doc.id,
        id: doc.id,
        updated_at: doc.meta.updated_at
      };
    });
    cb(null, res);
  });
}

// Create a new publication on the server
// -----------------

function createPublication(doc, cb) {
  console.log('creating publication...');
  $.ajax({
    type: 'POST',
    url: Substance.settings.hub + '/publications',
    data: {"muh": "meh"},
    success: function(result) {
      store.createPublication(doc.id, doc.content, cb);
    },
    error: function() {
      console.log('error when creating publication');
      cb('error');
    },
    dataType: 'json'
  });
}

// Delete publication by index for a given doc
// -----------------

function deletePublication(doc, index, cb) {
  $.ajax({
    type: 'DELETE',
    url: Substance.settings.hub + '/publications/'+doc.id+'/'+index,
    success: function(result) {
      store.deletePublication(doc.id, index, doc.content, cb);
    },
    error: function() {
      console.log('error when clearing publications');
      cb('error');
    },
    dataType: 'json'
  });
}

// Remove all publications from the server
// -----------------

function clearPublications(doc, cb) {
  console.log('clearing publications');
  $.ajax({
    type: 'DELETE',
    url: Substance.settings.hub + '/publications/'+doc.id,
    success: function(result) {
      store.clearPublications(doc.id, cb);
    },
    error: function() {
      console.log('error when clearing publications');
      cb('error');
    },
    dataType: 'json'
  });
}

// Create a new document
// -----------------

function createDocument(cb) {
  store.create(Math.uuid(), function(err, doc) {
    cb(err, new Substance.Session(doc));
  });
}

// Authenticate with your Substance user
// -----------------

function authenticate(options, cb) {
  // talk.send(["session:authenticate", options], function(err) {
  //   cb(err);
  // });
  cb(null);
}<commit_msg>Optimize.<commit_after>// Document.Store (web debug)
// -----------------

var StaticDocStore = function() {

};

StaticDocStore.prototype.get = function(id, cb) {
  // HACK: under webkitgtk XMLHttpRequests do not work for local resources :(
  //       workaround is to add a native extension that loads local data
  //       usage: substance.resources.getJSON(url)  - returns JS object
  //          or  substance.resources.get(url)      - returns content as String
  if (typeof substance !== "undefined" &&
    typeof substance.resources !== "undefined") {
     doc = substance.resources.getJSON('data/'+ (id+'.json'));
     cb(null, doc);
  } else {
    $.getJSON('data/'+ (id+'.json'), function(doc) {
      cb(null, doc);
    });
  }
};

StaticDocStore.prototype.list = function(cb) {
  cb(null, [
    { properties: { title: "Substance" }, id: "substance" },
    { properties: { title: "Hello World" }, id: "hello" }
  ]);
};

StaticDocStore.prototype.create = function(cb) {
  cb(null, {id: Math.uuid()});
};

StaticDocStore.prototype.update = function(id, operations, cb) {
  // Implement
};

StaticDocStore.prototype.delete = function(id, cb) {
  // Implement
};

// Initialize DocStore
// -----------------

var store;

// store = new StaticDocStore();

if (window.redis) {
  store = new redis.RedisDocStore();
} else {
  store = new StaticDocStore();
}

// Update doc (docstore.update)
// -----------------

function updateDoc(doc, commit, cb) {
  store.update(doc.id, [commit], function(err) {
    store.setSnapshot(doc.id, doc.content, 'master', function(err) {
      // Update metadata accordingly
      if (commit.op[0] === "set") {
        _.extend(doc.meta, doc.content.properties);
        updateMeta(doc, cb);
      };
    });
  });
};

function updateMeta(doc, cb) {
  doc.meta.updated_at = new Date();
  store.updateMeta(doc.id, doc.meta, cb);
}

// Comments
// -----------------
// Called by Substance.Text

var Comments = function(session) {
  this.session = session;
  this.scopes = [];
};

_.extend(Comments.prototype, _.Events, {
  compute: function(scope) {
    var node = this.session.node();
    this.scopes = [];

    if (node) {
      var content = this.session.document.content.nodes[node].content;
      var annotations = this.session.document.annotations(node);
    }

    this.commentsForNode(this.session.document, node, content, annotations, scope);
  },

  // Based on a new set of annotations (during editing)
  updateAnnotations: function(content, annotations) {
    var node = this.session.node();

    // Only consider markers as comment scopes
    var annotations = _.filter(annotations, function(a) {
      return _.include(["idea", "blur", "doubt"], a.type);
    });

    this.commentsForNode(this.session.document, node, content, annotations);
  },

  commentsForNode: function(document, node, content, annotations, scope) {
    this.scopes = [];

    // Extract annotation text from the model
    function annotationText(a) {
      if (!a.pos) return "No pos";
      return content.substr(a.pos[0], a.pos[1]);
    }

    if (node) {
      this.scopes.push({
        name: "Node",
        type: "node",
        id: "node_comments",
        comments: document.comments(node)
      });

      _.each(annotations, function(a) {
        this.scopes.push({
          name: annotationText(a),
          type: a.type,
          annotation: a.id,
          id: a.id,
          comments: document.commentsForAnnotation(a.id)
        });
      }, this);
    } else {
      this.scopes.push({
        id: "document_comments",
        name: "Document",
        type: "document",
        comments: document.documentComments()
      });
    }

    this.session.trigger('comments:updated', scope);
  }
});

// Substance.Session
// -----------------
//
// The Composer works with a session object, which maintains
// all the state of a document session
// TODO: No multiuser support yet, use app.user

Substance.Session = function(options) {
  this.document = new Substance.Document(options);

  this.users = {
    "michael": {
      "color": "#2F2B26",
      "selection": []
    }
  };

  this.selections = {};

  // Comments view
  this.comments = new Comments(this);

  // That's the current editor
  this.user = "michael";
};

_.extend(Substance.Session.prototype, _.Events, {
  select: function(nodes, options) {
    if (!options) options = {};
    var user = options.user || this.user; // Use current user by default

    // Do nothing if selection hasn't changed
    if (!this.selectionChanged(user, nodes, !!options.edit)) return;

    this.edit = !!options.edit;

    if (this.users[this.user].selection) {
      _.each(this.users[user].selection, function(node) {
        delete this.selections[node];
      }, this);
    }

    this.users[user].selection = nodes;
    _.each(nodes, function(node) {
      this.selections[node] = user;
    }, this);

    // New selection leads to new comment context
    this.comments.compute();
    this.trigger('node:selected');
  },

  // Publish/Republish document
  publish: function(cb) {
    var doc = this.document;
    doc.meta.published_at = new Date();
    doc.meta.published_commit = doc.getRef('master');

    // TODO: create publication
    createPublication(doc, function(err) {
      console.log('created publication');
      if (err) alert('Failed to create a publication.');
      updateMeta(doc, cb);
    });
  },

  // Unpublish document
  unpublish: function(cb) {
    var doc = this.document;
    delete doc.meta["published_at"];
    delete doc.meta["published_commit"];

    clearPublications(doc, function(err) {
      console.log('cleared publications');
      // TODO: remove all publications
      updateMeta(doc, cb);
    });
  },

  publishState: function() {
    var doc = this.document;
    if (!doc.meta.published_commit) return "unpublished";
    if (doc.getRef('master') === doc.meta.published_commit) return "published";
    return "dirty";
  },

  // Checks if selection has actually changed for a user
  selectionChanged: function(user, nodes, edit) {
    return !_.isEqual(nodes, this.selection(user)) || edit !== this.edit;
  },

  // Retrieve current node selection
  selection: function(user) {
    if (!user) user = this.user;
    return this.users[user].selection;
  },

  // Returns the node id of current active node
  // Only works if there's just one node selected
  node: function() {
    var lvl = this.level(),
        sel = this.selection();

    if (lvl >= 2 && sel.length === 1) {
      return sel[0];
    }
  },

  // Returns current navigation level (1..3)
  level: function() {
    var selection = this.users[this.user].selection;

    // Edit mode
    if (this.edit) return 3;

    // Selection mode (one or more nodes)
    if (selection.length >= 1) return 2;

    // no selection -> document level
    return 1;
  }
});

// Load a document
// -----------------

function loadDocument(id, cb) {
  store.get(id, function(err, doc) {
    cb(err, new Substance.Session(doc));
  });
}

// List all documents
// -----------------

function listDocuments(cb) {
  store.list(function(err, documents) {
    var res = _.map(documents, function(doc) {
      return {
        title: doc.meta.title,
        author: "le_author",
        file: doc.id,
        id: doc.id,
        updated_at: doc.meta.updated_at
      };
    });
    cb(null, res);
  });
}

// Create a new publication on the server
// -----------------

function createPublication(doc, cb) {
  $.ajax({
    type: 'POST',
    url: Substance.settings.hub + '/publications',
    data: {
      "document": doc.id,
      "data": JSON.stringify(doc.content)
    },
    success: function(result) {
      store.createPublication(doc.id, doc.content, cb);
    },
    error: function() {
      console.log('error when creating publication');
      cb('error');
    },
    dataType: 'json'
  });
}

// Delete publication by index for a given doc
// -----------------

function deletePublication(doc, index, cb) {
  $.ajax({
    type: 'DELETE',
    url: Substance.settings.hub + '/publications/'+doc.id+'/'+index,
    success: function(result) {
      store.deletePublication(doc.id, index, doc.content, cb);
    },
    error: function() {
      console.log('error when clearing publications');
      cb('error');
    },
    dataType: 'json'
  });
}

// Remove all publications from the server
// -----------------

function clearPublications(doc, cb) {
  console.log('clearing publications');
  $.ajax({
    type: 'DELETE',
    url: Substance.settings.hub + '/publications/'+doc.id,
    success: function(result) {
      store.clearPublications(doc.id, cb);
    },
    error: function() {
      console.log('error when clearing publications');
      cb('error');
    },
    dataType: 'json'
  });
}

// Create a new document
// -----------------

function createDocument(cb) {
  store.create(Math.uuid(), function(err, doc) {
    cb(err, new Substance.Session(doc));
  });
}

// Authenticate with your Substance user
// -----------------

function authenticate(options, cb) {
  // talk.send(["session:authenticate", options], function(err) {
  //   cb(err);
  // });
  cb(null);
}<|endoftext|>


<commit_before>/*
 * getMediaInfo functions
 *
 * Allow to retrieve meta data of servers supporting icecast
 * Also find out what type of media is being streamed (audio, video, etc)
 */

var EventEmitter  = require('events').EventEmitter;
var util          = require('util');
var icy           = require('icy');
var devnull       = require('dev-null');
if (process.env.NODE_ENV !== 'production'){
    require('longjohn');
}

const DISCOVER_ICY_METADATA = true;

var Connection = function (url2play, org_url2play, streamType, callback) {
    EventEmitter.call(this);
    this._url2play = url2play;
    this._callback = callback;
    this._media = {
        // Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType.
        contentId: url2play,
        streamType: streamType, // LIVE or BUFFERED

        // Title and cover displayed while buffering
        metadata: {
            type: 0,
            metadataType: 0,
            title: org_url2play
        }
    };

    //See if we have to run in dummy mode
    if (!DISCOVER_ICY_METADATA) {
        callback(this._media);
        return;
    }

    this._pipeOn = false;

    try{
        this._requestObject = icy.get(url2play, this._connected.bind(this));
        this._requestObject.on('error', function(e){
            console.log("MediaInformation - ICY connection error for "+this._url2play+": "+e);
        });
    } catch(e){
        console.log("MediaInformation - Cannot connect to "+this._url2play+": "+e);
    }
}

util.inherits(Connection, EventEmitter);

Connection.prototype.getMedia = function() {
    return this._media;
};

Connection.prototype.close = function() {
    if (this._requestObject){
        this._requestObject.abort();
    }
};

Connection.prototype._connected = function(res){
    console.log("MediaInformation - Connected to "+this._url2play);
    /*
     * Example from http://edge.live.mp3.mdn.newmedia.nacamar.net/ps-dieneue_rock/livestream_hi.mp3
     *
     * {'accept-ranges': 'none',
     *  'content-type': 'audio/mpeg',
     *  'icy-br': '128',
     *  'ice-audio-info': 'ice-samplerate=44100;ice-bitrate=128;ice-channels=2',
     *  'icy-description': 'BESTER ROCK UND POP',
     *  'icy-genre': 'Rock',
     *  'icy-name': 'DIE NEUE 107.7',
     *  'icy-pub': '1',
     *  'icy-url': 'http://www.dieneue1077.de',
     *  server: 'Icecast 2.3.3-kh11',
     *  'cache-control': 'no-cache, no-store',
     *  pragma: 'no-cache',
     *  'access-control-allow-origin': '*',
     *  'access-control-allow-headers': 'Origin, Accept, X-Requested-With, Content-Type',
     *  'access-control-allow-methods': 'GET, OPTIONS, HEAD',
     *  connection: 'close',
     *  expires: 'Mon, 26 Jul 1997 05:00:00 GMT',
     *  'icy-metaint': '16000' }
     */
    // log the HTTP response headers
    console.log(res.headers);

    if (res.headers &&
        res.headers["content-type"] &&
        ((res.headers["content-type"].indexOf("audio") >=0) ||
         (res.headers["content-type"].indexOf("video") >=0)))
        this._media.contentType = res.headers["content-type"];
    //console.log(this._media);

    if ("icy-name" in res.headers) {
        //As backup call the callback if we do not get
        //first metadata in less than 1 second
        this._timeoutHandler = setTimeout(function(){
            callback(this._media);
        },1000);

        // log any "metadata" events that happen
        res.on('metadata', this._gotMetadata.bind(this, res));
    } else {
        //Not ICY -> call callback already
        this._callback(this._media);
    }

}

Connection.prototype._gotMetadata = function(res, metadata) {
    /*
     * { StreamTitle: 'BILLY IDOL - WHITE WEDDING',
     StreamUrl: '&artist=BILLY%20IDOL&title=WHITE%20WEDDING&album=&duration=&songtype=S&overlay=&buycd=&website=&picture' }
     */
    var parsed = icy.parse(metadata);
    console.log(parsed);

    //Get title
    this._media.metadata.title = parsed.StreamTitle;

    //If we got media info then call callback already
    if (this._timeoutHandler){
        clearTimeout(this._timeoutHandler);
        this._timeoutHandler = undefined;
        this._callback(this._media);
    }

    //Notify that media has been updated
    this.emit("media", this._media);

    if (!this._pipeOn){
        //We need to keep reading in order to receive additional metadata
        //We do it here to avoid reading data for sources that do not send
        //any metadata
        res.pipe(devnull());
        this._pipeOn = true;
    }
};

var activeConnections = {};

function getMediaInfoListerner(callerId, url2play, org_url2play, streamType, callback){

    if (url2play in activeConnections &&
        callerId in activeConnections[url2play].callerIDs){

        //Call callback
        callback(activeConnections[url2play].connection.getMedia());

        //This callerID is already listing the URL
        return activeConnections[url2play].connection;
    }

    //Close in case this caller was already listing -> only one connection/callerID
    closeMediaInfoListerner(callerId);

    //If this is the first listener for this URL then create object
    if (!(url2play in activeConnections)) {
        var connection = new Connection(url2play, org_url2play, streamType, callback);
        activeConnections[url2play] = {
                callerIDs: {},
                connection: connection
        }
    }

    //Remember callerId
    activeConnections[url2play].callerIDs[callerId] = true;

    //Return connection
    return activeConnections[url2play].connection;
}

function closeMediaInfoListerner(callerId){
    for (var url2play in activeConnections){

        //Set this callerId as inactive
        activeConnections[url2play].callerIDs[callerId] = false;

        //Any active listeners for this URL?
        var anyListenerActive = false;
        for (var i in activeConnections[url2play].callerIDs){
            anyListenerActive |= activeConnections[url2play].callerIDs[i];
        }

        if (!anyListenerActive) {
            //None is listening to this URL -> destroy connection
            activeConnections[url2play].connection.close();
            activeConnections[url2play].callerIDs=undefined;
            console.log("Disconnect "+url2play);
            console.log("Last listener was "+callerId);
            delete activeConnections[url2play];
        }
    }
}

module.exports.getListener   = getMediaInfoListerner;
module.exports.closeListener = closeMediaInfoListerner;
<commit_msg>Correct callback<commit_after>/*
 * getMediaInfo functions
 *
 * Allow to retrieve meta data of servers supporting icecast
 * Also find out what type of media is being streamed (audio, video, etc)
 */

var EventEmitter  = require('events').EventEmitter;
var util          = require('util');
var icy           = require('icy');
var devnull       = require('dev-null');
if (process.env.NODE_ENV !== 'production'){
    require('longjohn');
}

const DISCOVER_ICY_METADATA = true;

var Connection = function (url2play, org_url2play, streamType, callback) {
    EventEmitter.call(this);
    this._url2play = url2play;
    this._callback = callback;
    this._media = {
        // Here you can plug an URL to any mp4, webm, mp3 or jpg file with the proper contentType.
        contentId: url2play,
        streamType: streamType, // LIVE or BUFFERED

        // Title and cover displayed while buffering
        metadata: {
            type: 0,
            metadataType: 0,
            title: org_url2play
        }
    };

    //See if we have to run in dummy mode
    if (!DISCOVER_ICY_METADATA) {
        callback(this._media);
        return;
    }

    this._pipeOn = false;

    try{
        this._requestObject = icy.get(url2play, this._connected.bind(this));
        this._requestObject.on('error', function(e){
            console.log("MediaInformation - ICY connection error for "+this._url2play+": "+e);
        });
    } catch(e){
        console.log("MediaInformation - Cannot connect to "+this._url2play+": "+e);
    }
}

util.inherits(Connection, EventEmitter);

Connection.prototype.getMedia = function() {
    return this._media;
};

Connection.prototype.close = function() {
    if (this._requestObject){
        this._requestObject.abort();
    }
};

Connection.prototype._connected = function(res){
    console.log("MediaInformation - Connected to "+this._url2play);
    /*
     * Example from http://edge.live.mp3.mdn.newmedia.nacamar.net/ps-dieneue_rock/livestream_hi.mp3
     *
     * {'accept-ranges': 'none',
     *  'content-type': 'audio/mpeg',
     *  'icy-br': '128',
     *  'ice-audio-info': 'ice-samplerate=44100;ice-bitrate=128;ice-channels=2',
     *  'icy-description': 'BESTER ROCK UND POP',
     *  'icy-genre': 'Rock',
     *  'icy-name': 'DIE NEUE 107.7',
     *  'icy-pub': '1',
     *  'icy-url': 'http://www.dieneue1077.de',
     *  server: 'Icecast 2.3.3-kh11',
     *  'cache-control': 'no-cache, no-store',
     *  pragma: 'no-cache',
     *  'access-control-allow-origin': '*',
     *  'access-control-allow-headers': 'Origin, Accept, X-Requested-With, Content-Type',
     *  'access-control-allow-methods': 'GET, OPTIONS, HEAD',
     *  connection: 'close',
     *  expires: 'Mon, 26 Jul 1997 05:00:00 GMT',
     *  'icy-metaint': '16000' }
     */
    // log the HTTP response headers
    console.log(res.headers);

    if (res.headers &&
        res.headers["content-type"] &&
        ((res.headers["content-type"].indexOf("audio") >=0) ||
         (res.headers["content-type"].indexOf("video") >=0)))
        this._media.contentType = res.headers["content-type"];
    //console.log(this._media);

    if ("icy-name" in res.headers) {
        //As backup call the callback if we do not get
        //first metadata in less than 1 second
        this._timeoutHandler = setTimeout(function(){
            this._callback(this._media);
        },1000);

        // log any "metadata" events that happen
        res.on('metadata', this._gotMetadata.bind(this, res));
    } else {
        //Not ICY -> call callback already
        this._callback(this._media);
    }

}

Connection.prototype._gotMetadata = function(res, metadata) {
    /*
     * { StreamTitle: 'BILLY IDOL - WHITE WEDDING',
     StreamUrl: '&artist=BILLY%20IDOL&title=WHITE%20WEDDING&album=&duration=&songtype=S&overlay=&buycd=&website=&picture' }
     */
    var parsed = icy.parse(metadata);
    console.log(parsed);

    //Get title
    this._media.metadata.title = parsed.StreamTitle;

    //If we got media info then call callback already
    if (this._timeoutHandler){
        clearTimeout(this._timeoutHandler);
        this._timeoutHandler = undefined;
        this._callback(this._media);
    }

    //Notify that media has been updated
    this.emit("media", this._media);

    if (!this._pipeOn){
        //We need to keep reading in order to receive additional metadata
        //We do it here to avoid reading data for sources that do not send
        //any metadata
        res.pipe(devnull());
        this._pipeOn = true;
    }
};

var activeConnections = {};

function getMediaInfoListerner(callerId, url2play, org_url2play, streamType, callback){

    if (url2play in activeConnections &&
        callerId in activeConnections[url2play].callerIDs){

        //Call callback
        callback(activeConnections[url2play].connection.getMedia());

        //This callerID is already listing the URL
        return activeConnections[url2play].connection;
    }

    //Close in case this caller was already listing -> only one connection/callerID
    closeMediaInfoListerner(callerId);

    //If this is the first listener for this URL then create object
    if (!(url2play in activeConnections)) {
        var connection = new Connection(url2play, org_url2play, streamType, callback);
        activeConnections[url2play] = {
                callerIDs: {},
                connection: connection
        }
    }

    //Remember callerId
    activeConnections[url2play].callerIDs[callerId] = true;

    //Return connection
    return activeConnections[url2play].connection;
}

function closeMediaInfoListerner(callerId){
    for (var url2play in activeConnections){

        //Set this callerId as inactive
        activeConnections[url2play].callerIDs[callerId] = false;

        //Any active listeners for this URL?
        var anyListenerActive = false;
        for (var i in activeConnections[url2play].callerIDs){
            anyListenerActive |= activeConnections[url2play].callerIDs[i];
        }

        if (!anyListenerActive) {
            //None is listening to this URL -> destroy connection
            activeConnections[url2play].connection.close();
            activeConnections[url2play].callerIDs=undefined;
            console.log("Disconnect "+url2play);
            console.log("Last listener was "+callerId);
            delete activeConnections[url2play];
        }
    }
}

module.exports.getListener   = getMediaInfoListerner;
module.exports.closeListener = closeMediaInfoListerner;
<|endoftext|>


<commit_before>var Trackr = require("trackr"),
	_ = require("underscore"),
	util = require("./util"),
	parse = require("./m+xml").parse,
	$track = require("./track").track;

var Model =
module.exports = function Model(data, parent, options) {
	this.proxies = [];
	this._dep = new Trackr.Dependency();
	if (Model.isModel(parent)) this.parent = parent;
	this.set(data, options && options.track);
}

Model.isModel = function(o) {
	return o instanceof Model;
}

Model.extend = util.subclass;

Model._defaultProxies = [ {
	isList:  true,
	match:   function(arr)    { return _.isArray(arr); },
	get:     function(arr, k) { return k === "length" ? this.length(arr) : arr[k]; },
	length:  function(arr)    { var len; return typeof(len = arr.$length) === "number" ? len : arr.length; },
	keys:    function(arr)    { return _.range(this.length(arr)); },
	isEmpty: function(arr)    { return !!this.length(arr); }
}, {
	match: function()     { return true; },
	get:   function(t, k) { if (t != null) return t[k]; }
} ];

Model.callProxyMethod = function(proxy, target, method, args, ctx) {
	var args = _.isArray(args) ? _.clone(args) : [];
	args.unshift(proxy, method, target);
	args.push(ctx);
	return util.result.apply(null, args);
}

_.extend(Model.prototype, {

	// sets the data on the model
	set: function(data, track) {
		if (track !== false) data = $track(data, track);
		this.data = data;
		this._dep.changed();
		return this;
	},

	// an array of models in the current stack, with the root as the first
	getAllModels: function() {
		var models = [ this ],
			model = this;

		while (model.parent) {
			models.unshift(model = model.parent);
		}

		return models
	},

	// gets the model in the stack at the index
	// negative values start at root
	getModelAtOffset: function(index) {
		if (!_.isNumber(index) || isNaN(index)) index = 0;
		if (index < 0) return this.getAllModels()[~index];

		var model = this;

		while (index && model) {
			model = model.parent;
			index--;
		}

		return model;
	},

	// gets the last model in the stack
	getRootModel: function() {
		var model = this;
		while (model.parent != null) model = model.parent;
		return model;
	},

	// returns the first model which passes the function
	findModel: function(fn) {
		var index = 0,
			model = this;

		while (model != null) {
			if (fn.call(this, model, index++)) return model;
			model = model.parent;
		}
	},

	// returns the value at path, but only looks in the data on this model
	getLocal: function(path, ctx) {
		if (typeof path === "string") path = parse(path, { startRule: "path" });
		if (path == null) path = { parts: [] };
		if (!_.isObject(path)) throw new Error("Expecting string or object for path.");
		if (ctx == null) ctx = this;

		var self = this;
		this._dep.depend();

		return _.reduce(path.parts, function(target, part) {
			target = self._get(target, part.key);

			_.each(part.children, function(k) {
				if (_.isObject(k)) k = ctx.get(k);
				target = self._get(target, k);
			});

			return target;
		}, this.data);
	},

	// retrieves value with path query
	get: function(paths) {
		var self = this;

		if (typeof paths === "string") paths = parse(paths, { startRule: "pathQuery" });
		if (!_.isArray(paths)) paths = paths != null ? [ paths ] : [];
		if (!paths.length) paths.push({ type: "all", parts: [] });

		return _.reduce(paths, function(result, path, index) {
			var model = self,
				scope = true,
				val;

			if (path.type === "root") {
				model = self.getRootModel();
			} else if (path.type === "parent") {
				model = self.getModelAtOffset(path.distance);
			} else if (path.type === "all") {
				scope = false;
			}

			if (model == null) return;

			while (_.isUndefined(val) && model != null) {
				val = model.getLocal(path, self);
				model = model.parent;
				if (scope) break;
			}

			if (_.isFunction(val)) {
				val = val.call(self, index === 0 ? null : result);
			}

			return val;
		}, void 0);
	},

	_get: function(target, key) {
		return this.callProxyMethod(this.getProxyByValue(target), target, "get", key);
	},

	proxy: function(key) {
		var proxy = this.getProxyByValue(this.data);
		if (key == null) return proxy;
		var args = _.toArray(arguments);
		args.unshift(proxy, this.data);
		return this.callProxyMethod.apply(this, args);
	},

	callProxyMethod: function(proxy, target, method) {
		return Model.callProxyMethod(proxy, target, method, Array.prototype.slice.call(arguments, 3), this);
	},

	getAllProxies: function() {
		var proxies = [],
			model = this;

		while (model != null) {
			proxies.push.apply(proxies, model.proxies);
			model = model.parent;
		}

		return proxies;
	},

	registerProxy: function(proxy) {
		if (typeof proxy !== "object" || proxy == null) throw new Error("Expecting object for proxy.");
		if (typeof proxy.match !== "function") throw new Error("Layer missing required match method.");
		if (typeof proxy.get !== "function") throw new Error("Layer missing required get method.");
		this.proxies.unshift(proxy);
		return this;
	},

	getProxyByValue: function(target) {
		var proxy;

		// look locally first
		proxy = _.find(this.proxies, function(p) {
			return p.match(target);
		});

		// then recursively check the parents
		if (proxy == null && this.parent != null) {
			proxy = this.parent.getProxyByValue(target);
		}

		// otherwise look through the defaults
		if (proxy == null) {
			proxy = _.find(Model._defaultProxies, function(p) {
				return p.match(target);
			});
		}

		return proxy;
	},

	// defines a symbolic property on an object that points to the data
	defineDataLink: function(obj, prop, options) {
		var model = this;

		Object.defineProperty(obj, prop, {
			configurable: options != null && options.configurable,
			enumerable: options == null || options.enumerable !== false,
			get: function() {
				model._dep.depend();
				return model.data;
			},
			set: function(val) {
				model.set(val);
			}
		});

		return obj;
	}

});
<commit_msg>improved model proxy methods<commit_after>var Trackr = require("trackr"),
	_ = require("underscore"),
	util = require("./util"),
	parse = require("./m+xml").parse,
	$track = require("./track").track;

var Model =
module.exports = function Model(data, parent, options) {
	this.proxies = [];
	this._dep = new Trackr.Dependency();
	if (Model.isModel(parent)) this.parent = parent;
	this.set(data, options && options.track);
}

Model.isModel = function(o) {
	return o instanceof Model;
}

Model.extend = util.subclass;

Model._defaultProxies = [ {
	isList:  true,
	match:   function(arr)    { return _.isArray(arr); },
	get:     function(arr, k) { return k === "length" ? this.length(arr) : arr[k]; },
	length:  function(arr)    { var len; return typeof(len = arr.$length) === "number" ? len : arr.length; },
	keys:    function(arr)    { return _.range(this.length(arr)); },
	isEmpty: function(arr)    { return !!this.length(arr); }
}, {
	match: function()     { return true; },
	get:   function(t, k) { if (t != null) return t[k]; }
} ];

Model.callProxyMethod = function(proxy, target, method, args, ctx) {
	var args = _.isArray(args) ? _.clone(args) : [];
	args.unshift(proxy, method, target);
	args.push(ctx);
	return util.result.apply(null, args);
}

_.extend(Model.prototype, {

	// sets the data on the model
	set: function(data, track) {
		if (track !== false) data = $track(data, track);
		this.data = data;
		this._dep.changed();
		return this;
	},

	// an array of models in the current stack, with the root as the first
	getAllModels: function() {
		var models = [ this ],
			model = this;

		while (model.parent) {
			models.unshift(model = model.parent);
		}

		return models
	},

	// gets the model in the stack at the index
	// negative values start at root
	getModelAtOffset: function(index) {
		if (!_.isNumber(index) || isNaN(index)) index = 0;
		if (index < 0) return this.getAllModels()[~index];

		var model = this;

		while (index && model) {
			model = model.parent;
			index--;
		}

		return model;
	},

	// gets the last model in the stack
	getRootModel: function() {
		var model = this;
		while (model.parent != null) model = model.parent;
		return model;
	},

	// returns the first model which passes the function
	findModel: function(fn) {
		var index = 0,
			model = this;

		while (model != null) {
			if (fn.call(this, model, index++)) return model;
			model = model.parent;
		}
	},

	// returns the value at path, but only looks in the data on this model
	getLocal: function(path, ctx) {
		if (typeof path === "string") path = parse(path, { startRule: "path" });
		if (path == null) path = { parts: [] };
		if (!_.isObject(path)) throw new Error("Expecting string or object for path.");
		if (ctx == null) ctx = this;

		var self = this;
		this._dep.depend();

		return _.reduce(path.parts, function(target, part) {
			target = self._get(target, part.key);

			_.each(part.children, function(k) {
				if (_.isObject(k)) k = ctx.get(k);
				target = self._get(target, k);
			});

			return target;
		}, this.data);
	},

	// retrieves value with path query
	get: function(paths) {
		var self = this;

		if (typeof paths === "string") paths = parse(paths, { startRule: "pathQuery" });
		if (!_.isArray(paths)) paths = paths != null ? [ paths ] : [];
		if (!paths.length) paths.push({ type: "all", parts: [] });

		return _.reduce(paths, function(result, path, index) {
			var model = self,
				scope = true,
				val;

			if (path.type === "root") {
				model = self.getRootModel();
			} else if (path.type === "parent") {
				model = self.getModelAtOffset(path.distance);
			} else if (path.type === "all") {
				scope = false;
			}

			if (model == null) return;

			while (_.isUndefined(val) && model != null) {
				val = model.getLocal(path, self);
				model = model.parent;
				if (scope) break;
			}

			if (_.isFunction(val)) {
				val = val.call(self, index === 0 ? null : result);
			}

			return val;
		}, void 0);
	},

	_get: function(target, key) {
		return this.callProxyMethod(this.getProxyByValue(target), target, "get", key);
	},

	proxy: function(key) {
		var proxy = this.getProxyByValue(this.data);
		if (key == null) return proxy;
		var args = _.toArray(arguments);
		args.unshift(proxy, this.data);
		return this.callProxyMethod.apply(this, args);
	},

	callProxyMethod: function(proxy, target, method) {
		return Model.callProxyMethod(proxy, target, method, Array.prototype.slice.call(arguments, 3), this);
	},

	getAllProxies: function() {
		var proxies = [],
			model = this;

		while (model != null) {
			proxies.push.apply(proxies, model.proxies);
			model = model.parent;
		}

		proxies.push.apply(proxies, Model._defaultProxies);

		return proxies;
	},

	hasProxy: function(proxy, proxies) {
		if (proxies == null) proxies = this.getAllProxies();
		return _.contains(proxies, proxy);
	},

	registerProxy: function(proxy) {
		if (typeof proxy !== "object" || proxy == null) throw new Error("Expecting object for proxy.");
		if (typeof proxy.match !== "function") throw new Error("Layer missing required match method.");
		if (typeof proxy.get !== "function") throw new Error("Layer missing required get method.");
		if (!this.hasProxy(proxy)) this.proxies.unshift(proxy);
		return this;
	},

	getProxyByValue: function(target, proxies) {
		if (proxies == null) proxies = this.getAllProxies();
		return _.find(proxies, function(proxy) {
			return proxy.match(target);
		});
	},

	// defines a reactive property on an object that points to the data
	defineDataLink: function(obj, prop, options) {
		var model = this;

		Object.defineProperty(obj, prop, {
			configurable: options != null && options.configurable,
			enumerable: options == null || options.enumerable !== false,
			get: function() {
				model._dep.depend();
				return model.data;
			},
			set: function(val) {
				model.set(val);
			}
		});

		return obj;
	}

});
<|endoftext|>


<commit_before>'use strict';

var bitcore = require('bitcore');

function builder(options) {
  /* jshint maxstatements: 50 */
  /* jshint maxcomplexity: 8 */

  if (!options) {
    options = {};
  }

  var magicNumber = options.magicNumber;
  if (!magicNumber) {
    magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
  }
  var Block = options.Block || bitcore.Block;
  var BlockHeader = options.BlockHeader || bitcore.BlockHeader;
  var Transaction = options.Transaction || bitcore.Transaction;
  var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock;
  var protocolVersion = options.protocolVersion || 70000;

  var commands = {};

  var exported = {
    constructors: {
      Block: Block,
      BlockHeader: BlockHeader,
      Transaction: Transaction,
      MerkleBlock: MerkleBlock
    },
    defaults: {
      protocolVersion: protocolVersion,
      magicNumber: magicNumber
    },
    commands: commands
  };

  commands.version = require('./commands/version')(options);
  commands.verack = require('./commands/verack')(options);
  commands.ping = require('./commands/ping')(options);
  commands.pong = require('./commands/pong')(options);
  commands.block = require('./commands/block')(options);
  commands.tx = require('./commands/tx')(options);
  commands.getdata = require('./commands/getdata')(options);
  commands.headers = require('./commands/headers')(options);
  commands.notfound = require('./commands/notfound')(options);
  commands.inv = require('./commands/inv')(options);
  commands.addr = require('./commands/addr')(options);
  commands.alert = require('./commands/alert')(options);
  commands.reject = require('./commands/reject')(options);
  commands.merkleblock = require('./commands/merkleblock')(options);
  commands.filterload = require('./commands/filterload')(options);
  commands.filteradd = require('./commands/filteradd')(options);
  commands.filterclear = require('./commands/filterclear')(options);
  commands.getblocks = require('./commands/getblocks')(options);
  commands.getheaders = require('./commands/getheaders')(options);
  commands.mempool = require('./commands/mempool')(options);
  commands.getaddr = require('./commands/getaddr')(options);

  return exported;

}

module.exports = builder;
<commit_msg>organized commands to build from an array<commit_after>'use strict';

var bitcore = require('bitcore');

function builder(options) {
  /* jshint maxstatements: 20 */
  /* jshint maxcomplexity: 10 */

  if (!options) {
    options = {};
  }

  var magicNumber = options.magicNumber;
  if (!magicNumber) {
    magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
  }
  var Block = options.Block || bitcore.Block;
  var BlockHeader = options.BlockHeader || bitcore.BlockHeader;
  var Transaction = options.Transaction || bitcore.Transaction;
  var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock;
  var protocolVersion = options.protocolVersion || 70000;

  var exported = {
    constructors: {
      Block: Block,
      BlockHeader: BlockHeader,
      Transaction: Transaction,
      MerkleBlock: MerkleBlock
    },
    defaults: {
      protocolVersion: protocolVersion,
      magicNumber: magicNumber
    },
    commands: {}
  };

  var commandsArray = [
    'version', 'verack', 'ping', 'pong', 'block', 'tx', 'getdata', 'headers', 'notfound',
    'inv', 'addr', 'alert', 'reject', 'merkleblock', 'filterload', 'filteradd', 'filterclear',
    'getblocks', 'getheaders', 'mempool', 'getaddr'
  ];

  for (var i = 0; i < commandsArray.length; i++) {
    var command = commandsArray[i];
    exported.commands[command] = require('./commands/' + command)(options);
  }

  return exported;

}

module.exports = builder;
<|endoftext|>


<commit_before>'use strict';

/*************************************************************
 * MIDDLEWARE
 ************************************************************/
//TODO: Load all files in this dir that are not index.js
//TODO: Load all files in any passed source directory.

let middleware = {
    poweredBy: require('./poweredBy'),
    compression: require('./compression'),
    // livereload: require('./livereload'),
    viewEngine: require('./viewEngine'),
    logger: require('./logger'),
    //TODO: cors support
    cors: require('./cors'),
    favicon: require('./favicon'),
    bodyParser: require('./bodyParser'),
    cookieParser: require('./cookieParser'),
    session: require('./session'),
    csrf: require('./csrf'),
    passport: require('./passport'),
    flash: require('./flash'),
    expressStatic: require('./expressStatic'),
    expressLayouts: require('./expressLayouts'),
    appVariables: require('./appVariables'),
    routes: require('./routes'),
    locals: require('./locals'),
    fileUpload: require('./fileUpload')
};

middleware.use = [
    'poweredBy',
    'compression',
    'viewEngine',
    'logger',
    'favicon',
    // 'cookieParser',
    'cors',
    /*
     * If you register static middleware after
     * the sesson middleware you will be
     * generating sessions for each static
     * file, i.e css/img/font/etc.
     */
    'expressStatic',
    'bodyParser',
    'session', //This must be before passport!!
    // 'livereload',
    'expressLayouts',
    'flash',
    'passport',
    'csrf',
    'appVariables',
    'routes',
    'locals',
    'fileUpload'
];

middleware.dependencies = [
    'trackfinder',
    'compression',
    'serve-favicon',
    'body-parser',
    'cookie-parser',
    'passport',
    'express-session',
    'express-ejs-layouts',
    'morgan',
    'ejs',
    'express-flash-2',
    'csurf',
    'skipper'
];

module.exports = middleware;
<commit_msg>Add requestId to middleware list<commit_after>'use strict';

/*************************************************************
 * MIDDLEWARE
 ************************************************************/
//TODO: Load all files in this dir that are not index.js
//TODO: Load all files in any passed source directory.

let middleware = {
    poweredBy: require('./poweredBy'),
    compression: require('./compression'),
    // livereload: require('./livereload'),
    viewEngine: require('./viewEngine'),
    logger: require('./logger'),
    //TODO: cors support
    cors: require('./cors'),
    favicon: require('./favicon'),
    bodyParser: require('./bodyParser'),
    cookieParser: require('./cookieParser'),
    session: require('./session'),
    csrf: require('./csrf'),
    passport: require('./passport'),
    flash: require('./flash'),
    expressStatic: require('./expressStatic'),
    expressLayouts: require('./expressLayouts'),
    requestId: require('./requestId'),
    appVariables: require('./appVariables'),
    routes: require('./routes'),
    locals: require('./locals'),
    fileUpload: require('./fileUpload')
};

middleware.use = [
    'poweredBy',
    'compression',
    'viewEngine',
    'logger',
    'favicon',
    // 'cookieParser',
    'cors',
    /*
     * If you register static middleware after
     * the sesson middleware you will be
     * generating sessions for each static
     * file, i.e css/img/font/etc.
     */
    'expressStatic',
    'requestId',
    'bodyParser',
    'session', //This must be before passport!!
    // 'livereload',
    'expressLayouts',
    'flash',
    'passport',
    'csrf',
    'appVariables',
    'routes',
    'locals',
    'fileUpload'
];

middleware.dependencies = [
    'trackfinder',
    'compression',
    'serve-favicon',
    'body-parser',
    'cookie-parser',
    'passport',
    'express-session',
    'express-ejs-layouts',
    'morgan',
    'ejs',
    'express-flash-2',
    'csurf',
    'skipper'
];

module.exports = middleware;
<|endoftext|>


<commit_before>(function () {
    var gli = glinamespace("gli");

    var EventSource = function (name) {
        this.name = name;
        this.listeners = [];
    };

    EventSource.prototype.addListener = function (target, callback) {
        this.listeners.push({
            target: target,
            callback: callback
        });
    };

    EventSource.prototype.removeListener = function (target, callback) {
        for (var n = 0; n < this.listeners.length; n++) {
            var listener = this.listeners[n];
            if ((listener.target === target) && (listener.callback === callback)) {
                this.listeners.splice(n, 1);
                break;
            }
        }
    };

    EventSource.prototype.fire = function () {
        for (var n = 0; n < this.listeners.length; n++) {
            var listener = this.listeners[n];
            try {
                listener.callback.apply(listener.target, arguments);
            } catch (e) {
                console.log("exception thrown in target of event " + this.name + ": " + e);
            }
        }
    };

    EventSource.prototype.fireDeferred = function () {
        var self = this;
        var args = arguments;
        setTimeout(function () {
            self.fire.apply(self, args);
        }, 0);
    };

    gli.EventSource = EventSource;

})();
<commit_msg>allow for mass unregistering<commit_after>(function () {
    var gli = glinamespace("gli");

    var EventSource = function (name) {
        this.name = name;
        this.listeners = [];
    };

    EventSource.prototype.addListener = function (target, callback) {
        this.listeners.push({
            target: target,
            callback: callback
        });
    };

    EventSource.prototype.removeListener = function (target, callback) {
        for (var n = 0; n < this.listeners.length; n++) {
            var listener = this.listeners[n];
            if (listener.target === target) {
                if (callback) {
                    if (listener.callback === callback) {
                        this.listeners.splice(n, 1);
                        break;
                    }
                } else {
                    this.listeners.splice(n, 1);
                }
            }
        }
    };

    EventSource.prototype.fire = function () {
        for (var n = 0; n < this.listeners.length; n++) {
            var listener = this.listeners[n];
            try {
                listener.callback.apply(listener.target, arguments);
            } catch (e) {
                console.log("exception thrown in target of event " + this.name + ": " + e);
            }
        }
    };

    EventSource.prototype.fireDeferred = function () {
        var self = this;
        var args = arguments;
        setTimeout(function () {
            self.fire.apply(self, args);
        }, 0);
    };

    gli.EventSource = EventSource;

})();
<|endoftext|>


<commit_before>/**
 * extends BaseModel with ACL functionality.
 */
var _ = require('lodash');
var when = require('when');
var BaseModel = require('./base_model');
var _debug = require('debug');
var logId = 'serverbone:models:acl';
var acl = require('../acl');
var errors = require('../errors');
var debug = {
  trace: _debug(logId + ':trace'),
  log: _debug(logId + ':log'),
  warn: _debug(logId + ':warn'),
  error: _debug(logId + ':error')
};

var ACLModel = BaseModel.extend({
  initialize: function (args, options) {
    ACLModel.__super__.initialize.apply(this, arguments);
    options = options || {};
    this._processPermissions();
    this.addRoles(options.roles);
    if (options.actor) {
      this.actor = options.actor;
    }
  },

  save: function(key, val, options) {
    var opts = options || {};
    if (key == null || typeof key === 'object') {
      opts = val || {};
    }
    opts.action = opts.action || this.isNew() ? 'create' : 'update';
    opts.actor = opts.actor || this.actor;
    return ACLModel.__super__.save.call(this, key, opts);
  },

  fetch: function(options) {
    options = options || {};
    options.action = options.action || 'read';
    options.actor = options.actor || this.actor;
    return ACLModel.__super__.fetch.call(this, options);
  },

  destroy: function(options) {
    options =  options || {};
    options.action = options.action || 'destroy';
    options.actor = options.actor || this.actor;
    var canAccess = this.canAccess(options.action, options.actor);
    if (!canAccess) return when.reject(new errors.ForbiddenError('No access to ' + options.action));
    return ACLModel.__super__.destroy.call(this, options);
  },
  /**
   * Add role/roles to this instance
   *
   * @param  {Object|Array} roles
   */
  addRoles: function(roles) {
    this.roles = this.roles || [];
    if (!roles) return;
    if (!_.isArray(roles)) roles = [roles];
    debug.log('Adding roles %s to model %s',_.uniq(this.roles.concat(roles)), this.url());
    this.roles = _.uniq(this.roles.concat(roles));
  },

  /**
   * Return any matching roles for model from this model.
   *
   * @param  {Object} actor
   * @return {Array}
   */
  getRoles: function(actor) {
    //debug.log('Model %s asking roles from %s',actor ? actor.url() : '', this.url());
    var roles = [];
    if (!actor) return roles;
    _.each(this.relationDefinitions, function (value, key) {
      var refs = _.pairs(value.references)[0];
      if (!value.references || !value.Class) return;

      if (actor.type === value.Class.prototype.type
        && actor.has(refs[0])
        && this.has(refs[1])
        && actor.get(refs[0]) === this.get(refs[1])) {
          roles = roles.concat(value.roles || key);
        }

    }, this);

    roles = _.uniq((actor.roles || []).concat(roles));
    return roles;
  },

  _processPermissions: function () {
    var permissions = {};
    if (this.schema && this.schema.permissions) {
      permissions = this.schema.permissions;
    }
    this.acl = new acl.ACL(permissions);
  },

  toJSON: function (options) {
    debug.log('toJSON: %s',JSON.stringify(options));
    options = options || {};
    var json = ACLModel.__super__.toJSON.apply(this, arguments);
    if (!options.actor && options.user) {
      options.actor = options.user || this.actor;
    }
    if (options.actor && this.acl) {
      var readableFields = this.propertiesWithAccess('read', options.actor);
      _.each(Object.keys(json), function (key) {
        if (readableFields.indexOf(key) === -1) delete json[key];
      });
    }
    return json;
  },

  // check access to this model
  canAccess: function (action, model) {
    debug.trace('canAccess %s', action);
    if (!this.acl) return true;
    if (!model) {
      debug.log('using default access model');
      model = new ACLModel();
    }
    debug.log('canAccess %s %s', action, model.url());
    return this.acl.assert(this.getRoles(model), action);
  },

  // Field level access: get properties which user has permissions
  // field can override object level access mask
  propertiesWithAccess: function (action, model) {
    if (!model) {
      debug.log('using default access model');
      model = new ACLModel();
    }
    debug.trace('propertiesWithAccess to action %s with model %s',action,model.toJSON());
    var objectAccess = this.canAccess(action, model);
    var keys = [];
    var self = this;
    if (!this.schema || !this.schema.properties) return keys;

    _.each(this.schema.properties, function (prop, key) {
      if (!prop.permissions && objectAccess) {
        keys.push(key);
      } else if (prop.permissions) {
        var mask = new acl.ACL(prop.permissions);
        var roles = self.getRoles(model);
        if (mask.assert(roles, action)) {
          debug.log('adding key %s because of %s for mask %s',key,roles.join(','), JSON.stringify(mask));
          keys.push(key);
        }
      }
    }, this);
    debug.log('propertiesWithAccess giving out keys: %s', keys.join(','));
    return keys;
  },

  validate: function (attrs, options) {
    debug.trace('validate: %s',JSON.stringify(options));
    var changedAttrs = this.changedAttributes();
    options = options ? _.clone(options) : {};
    debug.trace('validate: %s',JSON.stringify(options));
    var action = options.action || (this.isNew() ? 'create' : 'update');
    var actor = options.actor || options.user || options.model || this.actor;

    var noAccessTo, canAccess, changedProps, canActProps;
    if (this.acl) {
      canAccess = this.canAccess(action, actor);
      debug.log('we have an acl, canAccess: %s',canAccess);
      if (canAccess && changedAttrs) {
        changedProps = Object.keys(changedAttrs);
        // remove attributes not defined in the schema
        var schemaProps = Object.keys(this.schema.properties);
        changedProps = _.filter(changedProps, function(prop) {
          return schemaProps.indexOf(prop) > -1;
        }, this);
        canActProps = this.propertiesWithAccess(action, actor);
        noAccessTo = _.difference(changedProps, canActProps);
      }
      if ((!canAccess && changedAttrs) || (noAccessTo && noAccessTo.length > 0)) {
        debug.log('Failing validation, no access with action %s to %s',action, JSON.stringify(noAccessTo));
        var error = new errors.ForbiddenError('No access to ' + action + ' fields:' + noAccessTo);
        if (options && options.error) {
          if (options.error.length === 3) {
            options.error(this, error);
          } else {
            options.error(error);
          }
        }
        return error;
      }
    }
    return ACLModel.__super__.validate.apply(this, arguments);
  }
});

module.exports = ACLModel;<commit_msg>Use changedSinceSync for acl<commit_after>/**
 * extends BaseModel with ACL functionality.
 */
var _ = require('lodash');
var when = require('when');
var BaseModel = require('./base_model');
var _debug = require('debug');
var logId = 'serverbone:models:acl';
var acl = require('../acl');
var errors = require('../errors');
var debug = {
  trace: _debug(logId + ':trace'),
  log: _debug(logId + ':log'),
  warn: _debug(logId + ':warn'),
  error: _debug(logId + ':error')
};

var ACLModel = BaseModel.extend({
  initialize: function (args, options) {
    ACLModel.__super__.initialize.apply(this, arguments);
    options = options || {};
    this._processPermissions();
    this.addRoles(options.roles);
    if (options.actor) {
      this.actor = options.actor;
    }
  },

  save: function(key, val, options) {
    var opts = options || {};
    if (key == null || typeof key === 'object') {
      opts = val || {};
    }
    opts.action = opts.action || this.isNew() ? 'create' : 'update';
    opts.actor = opts.actor || this.actor;
    return ACLModel.__super__.save.call(this, key, opts);
  },

  fetch: function(options) {
    options = options || {};
    options.action = options.action || 'read';
    options.actor = options.actor || this.actor;
    return ACLModel.__super__.fetch.call(this, options);
  },

  destroy: function(options) {
    options =  options || {};
    options.action = options.action || 'destroy';
    options.actor = options.actor || this.actor;
    var canAccess = this.canAccess(options.action, options.actor);
    if (!canAccess) return when.reject(new errors.ForbiddenError('No access to ' + options.action));
    return ACLModel.__super__.destroy.call(this, options);
  },
  /**
   * Add role/roles to this instance
   *
   * @param  {Object|Array} roles
   */
  addRoles: function(roles) {
    this.roles = this.roles || [];
    if (!roles) return;
    if (!_.isArray(roles)) roles = [roles];
    debug.log('Adding roles %s to model %s',_.uniq(this.roles.concat(roles)), this.url());
    this.roles = _.uniq(this.roles.concat(roles));
  },

  /**
   * Return any matching roles for model from this model.
   *
   * @param  {Object} actor
   * @return {Array}
   */
  getRoles: function(actor) {
    //debug.log('Model %s asking roles from %s',actor ? actor.url() : '', this.url());
    var roles = [];
    if (!actor) return roles;
    _.each(this.relationDefinitions, function (value, key) {
      var refs = _.pairs(value.references)[0];
      if (!value.references || !value.Class) return;

      if (actor.type === value.Class.prototype.type
        && actor.has(refs[0])
        && this.has(refs[1])
        && actor.get(refs[0]) === this.get(refs[1])) {
          roles = roles.concat(value.roles || key);
        }

    }, this);

    roles = _.uniq((actor.roles || []).concat(roles));
    return roles;
  },

  _processPermissions: function () {
    var permissions = {};
    if (this.schema && this.schema.permissions) {
      permissions = this.schema.permissions;
    }
    this.acl = new acl.ACL(permissions);
  },

  toJSON: function (options) {
    debug.log('toJSON: %s',JSON.stringify(options));
    options = options || {};
    var json = ACLModel.__super__.toJSON.apply(this, arguments);
    if (!options.actor && options.user) {
      options.actor = options.user || this.actor;
    }
    if (options.actor && this.acl) {
      var readableFields = this.propertiesWithAccess('read', options.actor);
      _.each(Object.keys(json), function (key) {
        if (readableFields.indexOf(key) === -1) delete json[key];
      });
    }
    return json;
  },

  // check access to this model
  canAccess: function (action, model) {
    debug.trace('canAccess %s', action);
    if (!this.acl) return true;
    if (!model) {
      debug.log('using default access model');
      model = new ACLModel();
    }
    debug.log('canAccess %s %s', action, model.url());
    return this.acl.assert(this.getRoles(model), action);
  },

  // Field level access: get properties which user has permissions
  // field can override object level access mask
  propertiesWithAccess: function (action, model) {
    if (!model) {
      debug.log('using default access model');
      model = new ACLModel();
    }
    debug.trace('propertiesWithAccess to action %s with model %s',action,model.toJSON());
    var objectAccess = this.canAccess(action, model);
    var keys = [];
    var self = this;
    if (!this.schema || !this.schema.properties) return keys;

    _.each(this.schema.properties, function (prop, key) {
      if (!prop.permissions && objectAccess) {
        keys.push(key);
      } else if (prop.permissions) {
        var mask = new acl.ACL(prop.permissions);
        var roles = self.getRoles(model);
        if (mask.assert(roles, action)) {
          debug.log('adding key %s because of %s for mask %s',key,roles.join(','), JSON.stringify(mask));
          keys.push(key);
        }
      }
    }, this);
    debug.log('propertiesWithAccess giving out keys: %s', keys.join(','));
    return keys;
  },

  validate: function (attrs, options) {
    debug.trace('validate: %s',JSON.stringify(options));
    var changedAttrs = this.changedSinceSync ? this.changedSinceSync() : this.changedAttributes();
    options = options ? _.clone(options) : {};
    debug.trace('validate: %s',JSON.stringify(options));
    var action = options.action || (this.isNew() ? 'create' : 'update');
    var actor = options.actor || options.user || options.model || this.actor;

    var noAccessTo, canAccess, changedProps, canActProps;
    if (this.acl) {
      canAccess = this.canAccess(action, actor);
      debug.log('we have an acl, canAccess: %s',canAccess);
      if (canAccess && changedAttrs) {
        changedProps = Object.keys(changedAttrs);
        // remove attributes not defined in the schema
        var schemaProps = Object.keys(this.schema.properties);
        changedProps = _.filter(changedProps, function(prop) {
          return schemaProps.indexOf(prop) > -1;
        }, this);
        canActProps = this.propertiesWithAccess(action, actor);
        noAccessTo = _.difference(changedProps, canActProps);
      }
      if ((!canAccess && changedAttrs) || (noAccessTo && noAccessTo.length > 0)) {
        debug.log('Failing validation, no access with action %s to %s',action, JSON.stringify(noAccessTo));
        var error = new errors.ForbiddenError('No access to ' + action + ' fields:' + noAccessTo);
        if (options && options.error) {
          if (options.error.length === 3) {
            options.error(this, error);
          } else {
            options.error(error);
          }
        }
        return error;
      }
    }
    return ACLModel.__super__.validate.apply(this, arguments);
  }
});

module.exports = ACLModel;<|endoftext|>


<commit_before>function getAuthPin() {
  chrome.extension.sendRequest({check_pin_needed: 1}, function(response) {
    var fullText = $("div.message-content:not(.new_div)").text();
    if(fullText.match(/chromed bird/i) && !fullText.match(/denied/i)) {
      var pin = $.trim($("#oauth_pin").text());
      $("div.message-content").hide();
      var newDiv = $("
")
        .html("
Please wait, authorizing Chromed Bird...
Your PIN number is: " +  pin)
        .insertAfter("div.message-content");

      var nextOpacity = 1;
      function animateLoop() {
        if(nextOpacity == 1) nextOpacity = 0.3;
        else nextOpacity = 1;
        $("div.message-content.new_div").animate({opacity: nextOpacity}, 500, null, animateLoop);
      }
      animateLoop();

      chrome.extension.sendRequest({cr_oauth_pin: pin}, function(response) {
        $("div.message-content.new_div").css('opacity', 1);
        $("div.message-content.new_div").stop();
        if(response) {
          $("div.message-content.new_div").html(
            "
Congratulations, you've been successfully authenticated. Enjoy Chromed Bird!

Chromed Bird authorized!
");
        } else {
          $("div.message-content.new_div").html("
Oops... Something went wrong. Please, try clicking Chromed Bird icon again.
");
        }
      });
    }
  });
}
getAuthPin();
<commit_msg>Reset session cookie upon notification.<commit_after>function getAuthPin() {
  document.cookie = '_twitter_sess=';
  chrome.extension.sendRequest({check_pin_needed: 1}, function(response) {
    var fullText = $("div.message-content:not(.new_div)").text();
    if(fullText.match(/chromed bird/i) && !fullText.match(/denied/i)) {
      var pin = $.trim($("#oauth_pin").text());
      $("div.message-content").hide();
      var newDiv = $("
")
        .html("
Please wait, authorizing Chromed Bird...
Your PIN number is: " +  pin)
        .insertAfter("div.message-content");

      var nextOpacity = 1;
      function animateLoop() {
        if(nextOpacity == 1) nextOpacity = 0.3;
        else nextOpacity = 1;
        $("div.message-content.new_div").animate({opacity: nextOpacity}, 500, null, animateLoop);
      }
      animateLoop();

      chrome.extension.sendRequest({cr_oauth_pin: pin}, function(response) {
        $("div.message-content.new_div").css('opacity', 1);
        $("div.message-content.new_div").stop();
        if(response) {
          $("div.message-content.new_div").html(
            "
Congratulations, you've been successfully authenticated. Enjoy Chromed Bird!

Chromed Bird authorized!
");
        } else {
          $("div.message-content.new_div").html("
Oops... Something went wrong. Please, try clicking Chromed Bird icon again.
");
        }
      });
    }
  });
}
getAuthPin();
<|endoftext|>


<commit_before>'use strict';

const { merge } = require('sugarmerge');
const { on, off, SILENT_TOGGLE:SILENT } = require('../Toggle');
const { DEBUG } = require('../debug');
const { tag } = require('../tags');
const { copyKeys } = require('../util');
const Target = require('../Target');
const Unit = require('../Unit');

const bind = (fromPath, toPath, { Store: { get, set } }) => set(toPath, get(fromPath));
tag(bind, 'binding');

// TODO: use Unit factory instead of Target factory for more flexibility
const bindTo = (fromPath, targetName, { targets, Store: { get }, config:parentConfig }) => {
    const fn = targets[targetName];
    if (typeof fn === 'function') {
        const boundFn = (config, ...args) => {
            const binding = get(fromPath);
            return fn(merge(config, parentConfig, fn.config || {}, binding), ...args); // should get namespace from parent config instead of top-level merge?
        };
        return Target(copyKeys(fn, boundFn), targetName);
    } else {
        throw new Error('invalid fn name in bind-to');
    }
};
tag(bindTo, 'target');

const Exit = (name, toggle = false) => (fromPath, ...rest) => {
    const { Store: { get } } = rest.pop();
    const toPath = rest.pop();
    const value = get(fromPath, toggle);
    const compare = toPath
        ? get(toPath, toPath)
        : toggle;
    const test = typeof compare === 'boolean'
        ? value && ![ '0', 'false' ].includes(value)
        : compare == value;
    const result = toggle
        ? !test
        : test;
    if (result) {
        console.log(`Exiting. Reason: @${name} - ${fromPath}:${value} ${toPath ? `${toggle ? '!=' : '=='} ${toPath}:${compare}` : `is ${toggle ? 'falsy' : 'truthy'}`}`);
        process.exit();
    }
};

const exitWhen = Exit('exit-when');
tag(exitWhen, 'binding');

const proceedWhen = Exit('proceed-when', true);
tag(proceedWhen, 'binding');

const When = (name, toggle = false) => (fromPath, ...rest) => {
    const { targets, config, predicates = [], Store: { get }, ...restContext } = rest.pop();
    const targetName = rest.pop();
    const predicate = () => {
        const value = get(fromPath, toggle);
        const toPath = rest.pop();
        const compare = toPath
            ? get(toPath, toPath)
            : toggle;
        const test = typeof compare === 'boolean'
            ? value && ![ '0', 'false' ].includes(value)
            : compare == value;
        const result = toggle
            ? !test
            : test;
        return { result, reason: `${fromPath}:${value} ${toPath ? `${toggle ? '!=' : '=='} ${toPath}:${compare}` : `is ${toggle ? 'falsy' : 'truthy'}`}` };
    };
    return Unit({ arg: targets[targetName], targets, config, predicates: [ ...predicates, predicate ], name: targetName, ...restContext });
};

const when = When('when');
tag(when, 'target');

const whenNot = When('when-not', true);
tag(whenNot, 'target');

const log = (fromPath, { Store: { get } }) => Target({ fn: () => get(fromPath), name: '@log' });
tag(log, 'target');

const binding = {
    bind,
    'bind-to': bindTo,
    when,
    'when-not': whenNot,
    'exit-when': exitWhen,
    'proceed-when': proceedWhen,
    log
};

const ToggleOp = (name) => (v) => {
    const fns = {
        'on': () => on(name),
        'off': () => off(name)
    };
    if (fns[v]) fns[v]();
    return;
};

const toggles = {
    'silent': ToggleOp(SILENT),
    'debug': ToggleOp(DEBUG)
};

const special = {
    'prompts': () => {},
    'optional-prompts': () => {},
};

const operations = {
    ...binding,
    ...toggles,
    ...special
};

module.exports = operations;
<commit_msg>no more bind-to for now<commit_after>'use strict';

const { on, off, SILENT_TOGGLE:SILENT } = require('../Toggle');
const { DEBUG } = require('../debug');
const { tag } = require('../tags');
const Target = require('../Target');
const Unit = require('../Unit');

const bind = (fromPath, toPath, { Store: { get, set } }) => set(toPath, get(fromPath));
tag(bind, 'binding');

const Exit = (name, toggle = false) => (fromPath, ...rest) => {
    const { Store: { get } } = rest.pop();
    const toPath = rest.pop();
    const value = get(fromPath, toggle);
    const compare = toPath
        ? get(toPath, toPath)
        : toggle;
    const test = typeof compare === 'boolean'
        ? value && ![ '0', 'false' ].includes(value)
        : compare == value;
    const result = toggle
        ? !test
        : test;
    if (result) {
        console.log(`Exiting. Reason: @${name} - ${fromPath}:${value} ${toPath ? `${toggle ? '!=' : '=='} ${toPath}:${compare}` : `is ${toggle ? 'falsy' : 'truthy'}`}`);
        process.exit();
    }
};

const exitWhen = Exit('exit-when');
tag(exitWhen, 'binding');

const proceedWhen = Exit('proceed-when', true);
tag(proceedWhen, 'binding');

const When = (name, toggle = false) => (fromPath, ...rest) => {
    const { targets, config, predicates = [], Store: { get }, ...restContext } = rest.pop();
    const targetName = rest.pop();
    const predicate = () => {
        const value = get(fromPath, toggle);
        const toPath = rest.pop();
        const compare = toPath
            ? get(toPath, toPath)
            : toggle;
        const test = typeof compare === 'boolean'
            ? value && ![ '0', 'false' ].includes(value)
            : compare == value;
        const result = toggle
            ? !test
            : test;
        return { result, reason: `${fromPath}:${value} ${toPath ? `${toggle ? '!=' : '=='} ${toPath}:${compare}` : `is ${toggle ? 'falsy' : 'truthy'}`}` };
    };
    return Unit({ arg: targets[targetName], targets, config, predicates: [ ...predicates, predicate ], name: targetName, ...restContext });
};

const when = When('when');
tag(when, 'target');

const whenNot = When('when-not', true);
tag(whenNot, 'target');

const log = (fromPath, { Store: { get } }) => Target({ fn: () => get(fromPath, fromPath), name: '@log' });
tag(log, 'target');

const binding = {
    bind,
    when,
    'when-not': whenNot,
    'exit-when': exitWhen,
    'proceed-when': proceedWhen,
    log
};

const ToggleOp = (name) => (v) => {
    const fns = {
        'on': () => on(name),
        'off': () => off(name)
    };
    if (fns[v]) fns[v]();
    return;
};

const toggles = {
    'silent': ToggleOp(SILENT),
    'debug': ToggleOp(DEBUG)
};

const special = {
    'prompts': () => {},
    'optional-prompts': () => {},
};

const operations = {
    ...binding,
    ...toggles,
    ...special
};

module.exports = operations;
<|endoftext|>