<commit_before>var nativeNroonga = require('nroonga');
var nroonga = require('../wrapped-nroonga');
var IndexField = require('./index-field').IndexField;

exports.MINIMUM_NAME_LENGTH = 3;
exports.MAXIMUM_NAME_LENGTH = 28;
exports.INVALID_NAME_CHARACTER_PATTERN = /[^\-a-z0-9]/g;
exports.INVALID_TABLE_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;

var DEFAULT_ID =
      exports.DEFAULT_ID =
      Domain.DEFAULT_ID = '00000000000000000000000000';

function assertValidDomainName(domain) {
  if (typeof domain != 'string')
    throw new Error('domain name must be a string');

  if (domain.length < exports.MINIMUM_NAME_LENGTH)
    throw new Error('too short domain name (minimum length = ' +
                    exports.MINIMUM_NAME_LENGTH + ')');

  if (domain.length > exports.MAXIMUM_NAME_LENGTH)
    throw new Error('too long domain name (max length = ' +
                   exports.MAXIMUM_NAME_LENGTH + ')');

  var invalidCharacter = domain.match(exports.INVALID_NAME_CHARACTER_PATTERN) ||
                         domain.match(exports.INVALID_TABLE_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a domain name');
  }
}

// Accepts two arguments: source and context.
// You can give them in the reversed order, like:
// new Domain("source", context) or new Domain(context, "source")
function Domain() {
  var source, context;
  for (var i = 0, maxi = arguments.length, argument; i < maxi; i++) {
    argument = arguments[i];
    if (!argument) continue;
    if (argument instanceof nroonga.Context ||
        argument instanceof nativeNroonga.Database) {
      context = argument;
    } else if (argument) {
      source = argument;
    }
  }

  if (source instanceof Domain)
    return source;

  this.initialize(source, context);
}
Domain.prototype = {
  initialize: function(source, context) {
    this.cachedIndexFields = {};

    this.context = context && new nroonga.Context(context);
    this.initializeNameAndId(source);

    // for validation
    this.tableName;
    this.termsTableName;
  },
  initializeNameAndId: function(source) {
    if (typeof source == 'string') {
      this.name = source;
      this.id = this.getIdForTable(this.toTableNamePart(this.name));
      return;
    }

    if (source.query && source.query.DomainName) {
      this.name = source.query.DomainName;
      this.id = this.getIdForTable(this.toTableNamePart(this.name));
      return;
    }

    if (source.headers && source.headers.host) {
      var host = source.headers.host;
      var nameAndIdFromHost = Domain.getNameAndIdFromHost(host);
      if (nameAndIdFromHost.name) {
        this.name = nameAndIdFromHost.name;
        this.id = nameAndIdFromHost.id;
        return;
      }
    }

    if (source.url) {
      var nameAndIdFromPath = Domain.getNameAndIdFromPath(source.url);
      if (nameAndIdFromPath.name) {
        this.name = nameAndIdFromPath.name;
        this.id = nameAndIdFromPath.id;
        return;
      }
    }

    throw new Error('no domain name');
  },
  getIdForTable: function(tableName) {
    if (this.context) {
      var tables = this.context.tableListSync();
      var tableIdMatcher = new RegExp('^' + tableName + '_([^_]+)$');
      var id;
      if (tables.some(function(table) {
            var match = table.name.match(tableIdMatcher);
            if (match) {
              id = match[1];
              return true;
            }
            return false;
          }, this))
        return id;
    }
    return this.createNewId();
  },
  createNewId: function() {
    var size = 26;
    var characters = '0123456789abcdefghijklmnopqrstuvwxyz'.split('');
    var lastIndex = characters.length;
    var id = '';
    while (id.length < size) {
      id += characters[Math.round(Math.random() * lastIndex)];
    }
    return id;
  },

  get tableName() {
    if (!this._tableName) {
      assertValidDomainName(this.name);
      this._tableName = this.toTableNamePart(this.name) + '_' + this.id;
    }
    return this._tableName;
  },
  toTableNamePart: function(string) {
    return string;
  },

  get termsTableName() {
    if (!this._termsTableName)
      this._termsTableName = this.tableName + '_BigramTerms';
    return this._termsTableName;
  },

  getIndexField: function(field) {
    return this.cachedIndexFields[field] ||
           (this.cachedIndexFields[field] = new IndexField(field, this));
  },
  get indexFields() {
    if (!this.context)
      throw new Error('no context');
    var columns = this.context.ordinalColumnsSync(this.tableName);
    var fields = columns.map(function(column) {
                   // XXX The "name" must be the field name given by the user,
                   // not normalized. Because there is no such information in the
                   // context and currently the column name is luckily equals to the
                   // given field name, we can use the column name.
                   var name = column.name;
                   return this.getIndexField(name);
                 }, this);
    return fields;
  },

  get synonymTableName() {
    if (!this._synonymTableName)
      this._synonymTableName = this.tableName + '_synonyms';
    return this._synonymTableName;
  },

  get id() {
    return this._id === undefined ? DEFAULT_ID : this._id ;
  },
  set id(value) {
    return this._id = value;
  },

  get domainId() {
    return this.id + '/' + this.name;
  },

  get searchableDocumentsCount() {
    return 0;
  },
  get requiresIndexDocuments() {
    return false;
  },
  get searchInstanceCount() {
    return 0;
  },
  get searchPartitionCount() {
    return 0;
  },

  getDocumentsEndpoint: function(hostname) {
    return 'doc-' + this.name + '-' + this.id + hostname;
  },
  getSearchEndpoint: function(hostname) {
    return 'search-' + this.name + '-' + this.id + hostname;
  },

  createSync: function() {
    if (!this.context)
      throw new Error('no context');

    this.context.commandSync('table_create', {
      name: this.tableName,
      flags: nroonga.TABLE_HASH_KEY,
      key_type: nroonga.ShortText
    });
    this.context.commandSync('table_create', {
      name: this.termsTableName,
      flags: nroonga.TABLE_PAT_KEY + '|' + nroonga.KEY_NORMALIZE,
      key_type: nroonga.ShortText,
      default_tokenizer: nroonga.TokenBigram
    });
  },
  deleteSync: function() {
    if (!this.context)
      throw new Error('no context');

    this.context.commandSync('table_remove', {
      name: this.tableName
    });
    this.context.commandSync('table_remove', {
      name: this.termsTableName
    });
  },
  reindexSync: function() {
    this.indexFields.forEach(function(field) {
      field.reindexSync();
    });
  },
  updateSynonymsSync: function(synonymOptions) {
    if (!this.context)
      throw new Error('no context');
    var synonyms = synonymOptions.synonyms;

    try {
      this.context.commandSync('table_remove', {
        table: this.synonymTableName
      });
    } catch (error) {
      // The synonym table should be inexistent. Do nothing.
    }

    this.context.commandSync('table_create', {
      name: this.synonymTableName,
      flags: nroonga.TABLE_HASH_KEY,
      key_type: nroonga.ShortText,
      flags: nroonga.KEY_NORMALIZE
    });
    this.context.commandSync('column_create', {
      table: this.synonymTableName,
      name: 'synonyms',
      type: nroonga.ShortText,
      flags: nroonga.COLUMN_VECTOR
    });

    var load = Object.keys(synonyms).map(function(key) {
      return {_key: key, synonyms: synonyms[key]};
    });
    this.context.commandSync('load', {
      table: this.synonymTableName,
      values: JSON.stringify(load)
    });
  },
  isSynonymTableAvailableSync: function() {
    var results = this.context.commandSync('table_list');
    var tables = nroonga.formatResults(results);
    var self = this;
    return tables.some(function(table) {
      return table.name === self.synonymTableName;
    });
  }
};

exports.Domain = Domain;

Domain.getNameAndIdFromHost = function(host) {
  var domainMatcher = /^(?:doc|search)-([^\.]+)-([^\.\-]+)\./;
  var match = host.match(domainMatcher);
  if (match)
    return { name: match[1], id: match[2] };

  return { name: '', id: '' };
};

Domain.getNameAndIdFromPath = function(path) {
  var domainMatcher = /^\/gcs\/([^\/]+)-([^\/\-]+)/;

  var match = path.match(domainMatcher);
  if (match)
    return { name: match[1], id: match[2] };

  return { name: '', id: '' };
};
<commit_msg>Clear chached table names if domain id is changed<commit_after>var nativeNroonga = require('nroonga');
var nroonga = require('../wrapped-nroonga');
var IndexField = require('./index-field').IndexField;

exports.MINIMUM_NAME_LENGTH = 3;
exports.MAXIMUM_NAME_LENGTH = 28;
exports.INVALID_NAME_CHARACTER_PATTERN = /[^\-a-z0-9]/g;
exports.INVALID_TABLE_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;

var DEFAULT_ID =
      exports.DEFAULT_ID =
      Domain.DEFAULT_ID = '00000000000000000000000000';

function assertValidDomainName(domain) {
  if (typeof domain != 'string')
    throw new Error('domain name must be a string');

  if (domain.length < exports.MINIMUM_NAME_LENGTH)
    throw new Error('too short domain name (minimum length = ' +
                    exports.MINIMUM_NAME_LENGTH + ')');

  if (domain.length > exports.MAXIMUM_NAME_LENGTH)
    throw new Error('too long domain name (max length = ' +
                   exports.MAXIMUM_NAME_LENGTH + ')');

  var invalidCharacter = domain.match(exports.INVALID_NAME_CHARACTER_PATTERN) ||
                         domain.match(exports.INVALID_TABLE_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a domain name');
  }
}

// Accepts two arguments: source and context.
// You can give them in the reversed order, like:
// new Domain("source", context) or new Domain(context, "source")
function Domain() {
  var source, context;
  for (var i = 0, maxi = arguments.length, argument; i < maxi; i++) {
    argument = arguments[i];
    if (!argument) continue;
    if (argument instanceof nroonga.Context ||
        argument instanceof nativeNroonga.Database) {
      context = argument;
    } else if (argument) {
      source = argument;
    }
  }

  if (source instanceof Domain)
    return source;

  this.initialize(source, context);
}
Domain.prototype = {
  initialize: function(source, context) {
    this.cachedIndexFields = {};

    this.context = context && new nroonga.Context(context);
    this.initializeNameAndId(source);

    // for validation
    this.tableName;
    this.termsTableName;
  },
  initializeNameAndId: function(source) {
    if (typeof source == 'string') {
      this.name = source;
      this.id = this.getIdForTable(this.toTableNamePart(this.name));
      return;
    }

    if (source.query && source.query.DomainName) {
      this.name = source.query.DomainName;
      this.id = this.getIdForTable(this.toTableNamePart(this.name));
      return;
    }

    if (source.headers && source.headers.host) {
      var host = source.headers.host;
      var nameAndIdFromHost = Domain.getNameAndIdFromHost(host);
      if (nameAndIdFromHost.name) {
        this.name = nameAndIdFromHost.name;
        this.id = nameAndIdFromHost.id;
        return;
      }
    }

    if (source.url) {
      var nameAndIdFromPath = Domain.getNameAndIdFromPath(source.url);
      if (nameAndIdFromPath.name) {
        this.name = nameAndIdFromPath.name;
        this.id = nameAndIdFromPath.id;
        return;
      }
    }

    throw new Error('no domain name');
  },
  getIdForTable: function(tableName) {
    if (this.context) {
      var tables = this.context.tableListSync();
      var tableIdMatcher = new RegExp('^' + tableName + '_([^_]+)$');
      var id;
      if (tables.some(function(table) {
            var match = table.name.match(tableIdMatcher);
            if (match) {
              id = match[1];
              return true;
            }
            return false;
          }, this))
        return id;
    }
    return this.createNewId();
  },
  createNewId: function() {
    var size = 26;
    var characters = '0123456789abcdefghijklmnopqrstuvwxyz'.split('');
    var lastIndex = characters.length;
    var id = '';
    while (id.length < size) {
      id += characters[Math.round(Math.random() * lastIndex)];
    }
    return id;
  },

  get tableName() {
    if (!this._tableName) {
      assertValidDomainName(this.name);
      this._tableName = this.toTableNamePart(this.name) + '_' + this.id;
    }
    return this._tableName;
  },
  toTableNamePart: function(string) {
    return string;
  },

  get termsTableName() {
    if (!this._termsTableName)
      this._termsTableName = this.tableName + '_BigramTerms';
    return this._termsTableName;
  },

  getIndexField: function(field) {
    return this.cachedIndexFields[field] ||
           (this.cachedIndexFields[field] = new IndexField(field, this));
  },
  get indexFields() {
    if (!this.context)
      throw new Error('no context');
    var columns = this.context.ordinalColumnsSync(this.tableName);
    var fields = columns.map(function(column) {
                   // XXX The "name" must be the field name given by the user,
                   // not normalized. Because there is no such information in the
                   // context and currently the column name is luckily equals to the
                   // given field name, we can use the column name.
                   var name = column.name;
                   return this.getIndexField(name);
                 }, this);
    return fields;
  },

  get synonymTableName() {
    if (!this._synonymTableName)
      this._synonymTableName = this.tableName + '_synonyms';
    return this._synonymTableName;
  },

  get id() {
    return this._id === undefined ? DEFAULT_ID : this._id ;
  },
  set id(value) {
    this._id = value;
    // clear caches
    delete this._tableName;
    delete this._termsTableName;
    delete this._synonymTableName;
    return value;
  },

  get domainId() {
    return this.id + '/' + this.name;
  },

  get searchableDocumentsCount() {
    return 0;
  },
  get requiresIndexDocuments() {
    return false;
  },
  get searchInstanceCount() {
    return 0;
  },
  get searchPartitionCount() {
    return 0;
  },

  getDocumentsEndpoint: function(hostname) {
    return 'doc-' + this.name + '-' + this.id + hostname;
  },
  getSearchEndpoint: function(hostname) {
    return 'search-' + this.name + '-' + this.id + hostname;
  },

  createSync: function() {
    if (!this.context)
      throw new Error('no context');

    this.context.commandSync('table_create', {
      name: this.tableName,
      flags: nroonga.TABLE_HASH_KEY,
      key_type: nroonga.ShortText
    });
    this.context.commandSync('table_create', {
      name: this.termsTableName,
      flags: nroonga.TABLE_PAT_KEY + '|' + nroonga.KEY_NORMALIZE,
      key_type: nroonga.ShortText,
      default_tokenizer: nroonga.TokenBigram
    });
  },
  deleteSync: function() {
    if (!this.context)
      throw new Error('no context');

    this.context.commandSync('table_remove', {
      name: this.tableName
    });
    this.context.commandSync('table_remove', {
      name: this.termsTableName
    });
  },
  reindexSync: function() {
    this.indexFields.forEach(function(field) {
      field.reindexSync();
    });
  },
  updateSynonymsSync: function(synonymOptions) {
    if (!this.context)
      throw new Error('no context');
    var synonyms = synonymOptions.synonyms;

    try {
      this.context.commandSync('table_remove', {
        table: this.synonymTableName
      });
    } catch (error) {
      // The synonym table should be inexistent. Do nothing.
    }

    this.context.commandSync('table_create', {
      name: this.synonymTableName,
      flags: nroonga.TABLE_HASH_KEY,
      key_type: nroonga.ShortText,
      flags: nroonga.KEY_NORMALIZE
    });
    this.context.commandSync('column_create', {
      table: this.synonymTableName,
      name: 'synonyms',
      type: nroonga.ShortText,
      flags: nroonga.COLUMN_VECTOR
    });

    var load = Object.keys(synonyms).map(function(key) {
      return {_key: key, synonyms: synonyms[key]};
    });
    this.context.commandSync('load', {
      table: this.synonymTableName,
      values: JSON.stringify(load)
    });
  },
  isSynonymTableAvailableSync: function() {
    var results = this.context.commandSync('table_list');
    var tables = nroonga.formatResults(results);
    var self = this;
    return tables.some(function(table) {
      return table.name === self.synonymTableName;
    });
  }
};

exports.Domain = Domain;

Domain.getNameAndIdFromHost = function(host) {
  var domainMatcher = /^(?:doc|search)-([^\.]+)-([^\.\-]+)\./;
  var match = host.match(domainMatcher);
  if (match)
    return { name: match[1], id: match[2] };

  return { name: '', id: '' };
};

Domain.getNameAndIdFromPath = function(path) {
  var domainMatcher = /^\/gcs\/([^\/]+)-([^\/\-]+)/;

  var match = path.match(domainMatcher);
  if (match)
    return { name: match[1], id: match[2] };

  return { name: '', id: '' };
};
<|endoftext|>


<commit_before>var firebase = require('firebase');
var urls = require('./helpers/utils').urls;

var store_request = function(url, sender, loc){
    var ref = new firebase(url);
    ref.push().set({
        sender:sender,
        loc: loc,
        time: firebase.ServerValue.TIMESTAMP
    });
}

var command = function(request, response){
    var sender = request.body['From'];
    var msg = request.body['Body'];

    if(msg.match(/FOOD/i)){
        store_request(urls.food, sender, "location");
    }
    if(msg.match(/water/i)){
        store_request(urls.water, sender, "location");
    }
    if(msg.match(/shelter/i)){
        store_request(urls.food, sender, "location");
    }

    response.send(twiml.message('HEY'));
}

module.exports=command;<commit_msg>bugfix<commit_after>var firebase = require('firebase');
var urls = require('./helpers/utils').urls;
var twilio = require('twilio');

var store_request = function(url, sender, loc){
    var ref = new firebase(url);
    ref.push().set({
        sender:sender,
        loc: loc,
        time: firebase.ServerValue.TIMESTAMP
    });
}

var command = function(request, response){
    var sender = request.body['From'];
    var msg = request.body['Body'];

    if(msg.match(/FOOD/i)){
        store_request(urls.food, sender, "location");
    }
    if(msg.match(/water/i)){
        store_request(urls.water, sender, "location");
    }
    if(msg.match(/shelter/i)){
        store_request(urls.food, sender, "location");
    }

    response.send(twiml.message('HEY'));
}

module.exports=command;<|endoftext|>


<commit_before>/*
 * PEG.js @VERSION
 *
 * http://pegjs.majda.cz/
 *
 * Copyright (c) 2010-2012 David Majda
 * Licensend under the MIT license.
 */
(function(undefined) {

var PEG = {
  /* PEG.js version. */
  VERSION: "@VERSION",

  /*
   * Generates a parser from a specified grammar and returns it.
   *
   * The grammar must be a string in the format described by the metagramar in
   * the parser.pegjs file.
   *
   * Throws |PEG.parser.SyntaxError| if the grammar contains a syntax error or
   * |PEG.GrammarError| if it contains a semantic error. Note that not all
   * errors are detected during the generation and some may protrude to the
   * generated parser and cause its malfunction.
   */
  buildParser: function(grammar, options) {
    return PEG.compiler.compile(PEG.parser.parse(grammar), options);
  }
};

/* Thrown when the grammar contains an error. */

PEG.GrammarError = function(message) {
  this.name = "PEG.GrammarError";
  this.message = message;
};

PEG.GrammarError.prototype = Error.prototype;

// @include "utils.js"
// @include "parser.js"
// @include "compiler.js"

if (typeof module === "object") {
  module.exports = PEG;
} else if (typeof window === "object") {
  window.PEG = PEG;
} else {
  throw new Error("Can't export PEG library (no \"module\" nor \"window\" object detected).");
}

})();
<commit_msg>Change how the library is exported<commit_after>/*
 * PEG.js @VERSION
 *
 * http://pegjs.majda.cz/
 *
 * Copyright (c) 2010-2012 David Majda
 * Licensend under the MIT license.
 */
var PEG = (function(undefined) {

var PEG = {
  /* PEG.js version. */
  VERSION: "@VERSION",

  /*
   * Generates a parser from a specified grammar and returns it.
   *
   * The grammar must be a string in the format described by the metagramar in
   * the parser.pegjs file.
   *
   * Throws |PEG.parser.SyntaxError| if the grammar contains a syntax error or
   * |PEG.GrammarError| if it contains a semantic error. Note that not all
   * errors are detected during the generation and some may protrude to the
   * generated parser and cause its malfunction.
   */
  buildParser: function(grammar, options) {
    return PEG.compiler.compile(PEG.parser.parse(grammar), options);
  }
};

/* Thrown when the grammar contains an error. */

PEG.GrammarError = function(message) {
  this.name = "PEG.GrammarError";
  this.message = message;
};

PEG.GrammarError.prototype = Error.prototype;

// @include "utils.js"
// @include "parser.js"
// @include "compiler.js"

return PEG;

})();

if (typeof module !== "undefined") {
  module.exports = PEG;
}
<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.10.0
var HotpStrategy, LocalStrategy, TotpStrategy, User, bcrypt, passport;

bcrypt = require('bcrypt');

passport = require('passport');

LocalStrategy = require('passport-local').Strategy;

HotpStrategy = require('passport-hotp').Strategy;

TotpStrategy = require('passport-totp').Strategy;

User = require('../models/user');

module.exports = function() {
  var options;
  passport.currentUser = null;
  passport.serializeUser = function(user, req, done) {
    return done(null, user._id);
  };
  passport.deserializeUser = function(id, req, done) {
    if ((passport.currentUser != null) && id === passport.currentUser._id) {
      return done(null, passport.currentUser);
    } else {
      return done(null, false);
    }
  };
  options = {
    usernameField: 'password'
  };
  passport.use(new LocalStrategy(options, function(email, password, done) {
    return User.first(function(err, user) {
      if ((err != null) || (user == null)) {
        return done(err, false);
      } else {
        return bcrypt.compare(password, user.password, function(err, result) {
          if (err != null) {
            return done(err, false);
          } else if (result) {
            passport.currentUser = user;
            passport.currentUser.id = user._id;
            return done(err, user);
          } else {
            return done(err, false);
          }
        });
      }
    });
  }));
  passport.use(new HotpStrategy({
    codeField: "authcode"
  }, function(user, done) {
    return done(null, new Buffer(user.encryptedOtpKey, 'hex'), user.hotpCounter);
  }, function(user, key, counter, delta, done) {
    if (counter > user.hotpCounter) {
      return User.updateAttributes(user._id, {
        hotpCounter: counter
      }, function(err) {
        return done(err);
      });
    } else {
      return done("error otp weak counter");
    }
  }));
  return passport.use(new TotpStrategy({
    codeField: "authcode"
  }, function(user, done) {
    return done(null, user.encryptedOtpKey, 30);
  }));
};
<commit_msg>built<commit_after>// Generated by CoffeeScript 1.10.0
var HotpStrategy, LocalStrategy, TotpStrategy, User, bcrypt, passport;

bcrypt = require('bcrypt');

passport = require('passport');

LocalStrategy = require('passport-local').Strategy;

HotpStrategy = require('passport-hotp').Strategy;

TotpStrategy = require('passport-totp').Strategy;

User = require('../models/user');

module.exports = function() {
  var options;
  passport.currentUser = null;
  passport.serializeUser = function(user, req, done) {
    return done(null, user._id);
  };
  passport.deserializeUser = function(id, req, done) {
    if ((passport.currentUser != null) && id === passport.currentUser._id) {
      return done(null, passport.currentUser);
    } else {
      return done(null, false);
    }
  };
  options = {
    usernameField: 'password'
  };
  passport.use(new LocalStrategy(options, function(email, password, done) {
    return User.first(function(err, user) {
      if ((err != null) || (user == null)) {
        return done(err, false);
      } else {
        return bcrypt.compare(password, user.password, function(err, result) {
          if (err != null) {
            return done(err, false);
          } else if (result) {
            passport.currentUser = user;
            passport.currentUser.id = user._id;
            return done(err, user);
          } else {
            return done(err, false);
          }
        });
      }
    });
  }));
  passport.use(new HotpStrategy({
    codeField: "authcode"
  }, function(user, done) {
    return done(null, new Buffer(user.encryptedOtpKey, 'hex'), user.hotpCounter);
  }, function(user, key, counter, delta, done) {
    if (counter > user.hotpCounter) {
      return User.updateAttributes(user._id, {
        hotpCounter: counter
      }, function(err) {
        return done(err);
      });
    } else {
      return done("error otp weak counter");
    }
  }));
  return passport.use(new TotpStrategy({
    codeField: "authcode"
  }, function(user, done) {
    return done(null, new Buffer(user.encryptedOtpKey, 'hex'), 30);
  }));
};
<|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-2015 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';

/**
 * Bakery holds the context for making HTTP requests
 * that automatically acquire and discharge macaroons.
 *
 * @module env
 * @submodule env.bakery
 */

YUI.add('juju-env-bakery', function(Y) {

  var module = Y.namespace('juju.environments.web');
  var macaroon = Y.namespace('macaroon');

  /**
   * Bakery client inspired by the equivalent GO code.
   *
   * This object exposes the ability to perform requests
   * that automatically acquire and discharge macaroons
   *
   * @class Bakery
   */

  var Bakery = Y.Base.create('Bakery',
      Y.Base, [], {

      /**
       Initialize.

       @method initializer
       @param {Object} cfg A config object providing webhandler and visit method
           information. A visitMethod can be provided, which becomes the
           bakery's visitMethod. Alternatively, the bakery can be configured to
           non interactive mode. If neither is true, the default method is used.
           {visitMethod: fn, interactive: boolean,
            webhandler: obj, serviceName: string}
       @return {undefined} Nothing.
       */
      initializer: function (cfg) {
        this.webhandler = cfg.webhandler;
        if (cfg.visitMethod) {
          this.visitMethod = cfg.visitMethod;
        } else if (cfg.interactive !== undefined && !cfg.interactive) {
          this.visitMethod = this._nonInteractiveVisitMethod;
        } else {
          this.visitMethod = this._defaultVisitMethod;
        }
        this.macaroonName = 'Macaroons-' + cfg.serviceName;
        this.setCookiePath = cfg.setCookiePath;
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 except 401/407 where it does authentication.
       */
      sendGetRequest: function (path, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {'Bakery-Protocol-Version': 1};
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendGetRequest(
          path, headers, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this,
            path,
            successCallback,
            failureCallback
          )
        );
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param path {String} The path to make the api request to.
       @param data {String} Stringified JSON of parameters to send to the POST
           endpoint.
       @param successCallback {Function} Called when the api request completes
           successfully.
       @param failureCallback {Function} Called when the api request fails
           with a response of >= 400 except 401/407 where it does
           authentication.
       */
      sendPostRequest: function (path, data, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-type': 'application/json'
        };
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendPostRequest(
          path, headers, data, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this, path, successCallback, failureCallback)
        );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback. For 407/401
       response it will request authentication through the macaroon provided in
       the 401/407 response.

       @method _requestHandlerWithInteraction
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       @param {Object} response The XHR response object.
       */
      _requestHandlerWithInteraction: function (path, successCallback,
                                                failureCallback, response) {
        var target = response.target;
        if (target.status === 401 &&
          target.getResponseHeader('Www-Authenticate') === 'Macaroon') {
          var jsonResponse = JSON.parse(target.responseText);
          this._authenticate(
            jsonResponse.Info.Macaroon,
            this._sendOriginalRequest.bind(
              this, path, successCallback, failureCallback
            ),
            failureCallback
          );
        } else {
          this._requestHandler(successCallback, failureCallback, response);
        }
      },

      /**
       Used to resend the original request without any interaction this time..

       @method _sendOriginalRequest
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       */
      _sendOriginalRequest: function(path, successCallback, failureCallback) {
          var macaroons = this._getMacaroon();
          var headers = {'Bakery-Protocol-Version': 1};
          if (macaroons !== null) {
            headers['Macaroons'] = macaroons;
          }
          this.webhandler.sendGetRequest(
            path, headers, null, null, false, null,
            this._requestHandler.bind(
              this, successCallback, failureCallback
            )
          );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback.

       @method _requestHandler
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400.
       @param {Object} response The XHR response object.
       */
      _requestHandler: function (successCallback, failureCallback, response) {
        var target = response.target;
        if (target.status >= 400) {
          failureCallback(response);
          return;
        }
        successCallback(response);
      },

      /**
       Authenticate by discharging the macaroon and
       then set the cookie by calling the authCookiePath provided

       @method authenticate
       @param {Macaroon} The macaroon to be discharged
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _authenticate: function (m, requestFunction, failureCallback) {
        try {
          macaroon.discharge(
            macaroon.import(m),
            this._obtainThirdPartyDischarge.bind(this),
            this._processDischarges.bind(
              this,
              requestFunction,
              failureCallback
            ),
            failureCallback
          );
        } catch (ex) {
          failureCallback(ex.message);
        }
      },

      /**
       Process the discharged macaroon and call the end point to be able to set
       a cookie for the origin domain only when an auth cookie path is
       provided, then call the original function.

       @method _processDischarges
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       @param {[Macaroon]} The macaroons being discharged
       */
      _processDischarges: function (requestFunction, failureCallback,
                                    discharges) {
        var jsonMacaroon;
        try {
          jsonMacaroon = macaroon.export(discharges);
        } catch (ex) {
          failureCallback(ex.message);
        }
        var content = JSON.stringify({'Macaroons': jsonMacaroon});
        if (this.setCookiePath === undefined) {
          this._setMacaroonsCookie(requestFunction, jsonMacaroon);
          return;
        }
        this.webhandler.sendPutRequest(
          this.setCookiePath,
          null, content, null, null, true, null,
          this._requestHandler.bind(
            this,
            this._setMacaroonsCookie.bind(this, requestFunction, jsonMacaroon),
            failureCallback
          )
        );
      },

      /**
       Process successful discharge by setting Macaroons Cookie
       and invoke the original request

       @method _setMacaroonsCookie
       @param {Function} The path where to send put request
              to set the cookie back
       @param {Object} an exported Macaroon
       */
      _setMacaroonsCookie: function (originalRequest, jsonMacaroon) {
        var prefix = this.macaroonName + '=';
        document.cookie = prefix + btoa(JSON.stringify(jsonMacaroon));
        originalRequest();
      },

      /**
       Go to the discharge endpoint to obtain the third party discharge.

       @method obtainThirdPartyDischarge
       @param {String} The origin location
       @param {String} The third party location where to discharge
       @param {Function} The macaroon to be discharge
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _obtainThirdPartyDischarge: function (location,
                                            thirdPartyLocation, condition,
                                            successCallback, failureCallback) {
        thirdPartyLocation += '/discharge';
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-Type': 'application/x-www-form-urlencoded'
        };
        var content = 'id=' + encodeURI(condition) +
          '&location=' + encodeURI(location);
        this.webhandler.sendPostRequest(
          thirdPartyLocation,
          headers, content, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            this._interact.bind(this, successCallback, failureCallback)
          )
        );
      },

    /**
      Get a JSON response from authentication either trusted or with
      interaction that contains a macaroon.

      @method _exportMacaroon
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
      @param {Object} response The XHR response object.
    */
    _exportMacaroon: function (successCallback, failureCallback, response) {
      try {
        var dm = macaroon.import(
          JSON.parse(response.target.responseText).Macaroon
        );
        successCallback(dm);
      } catch (ex) {
        failureCallback(ex.message);
      }
    },

    /**
      Interact to be able to sign-in to get authenticated.

      @method _interact
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
    */
    _interact: function(successCallback, failureCallback, e) {
      var response = JSON.parse(e.target.responseText);
      if (response.Code !== 'interaction required') {
        failureCallback(response.Code);
        return;
      }

      this.visitMethod(response);

      this.webhandler.sendGetRequest(
          response.Info.WaitURL,
          null, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            failureCallback
          )
      );
    },

    /**
      Non interactive visit method which sends the jujugui "auth" blob
      to the IdM to login.

      @method _nonInteractiveVisitMethod
      @param {Object} response An xhr response object.
    */
    _nonInteractiveVisitMethod: function(response) {
      var acceptHeaders = {'Accept': 'application/json'};
      var contentHeaders = {'Content-Type': 'application/json'};
      var login = function(response) {
        var method = JSON.parse(response.target.responseText).jujugui;
        var data = JSON.stringify({login: window.juju_config.auth});
        this.webhandler.sendPostRequest(
            method, contentHeaders, data,
            null, null, false, null, null);
      };

      this.webhandler.sendGetRequest(
          response.Info.VisitURL,
          acceptHeaders, null, null, false, null, login.bind(this));
    },

    /**
      Default visit method which is to open a window to the
      response.Info.VisitURL.

      @method _defaultVisitMethod
      @param {Object} response An xhr response object.
    */
    _defaultVisitMethod: function(response) {
      window.open(response.Info.VisitURL, 'Login');
    },

    /**
      Get macaroon from local cookie.

      @method _getMacaroon
      @return {String} Macaroons that was set in local cookie.
    */
    _getMacaroon: function() {
      var name = this.macaroonName + '=';
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
      }
      return null;
    }
  });

  module.Bakery = Bakery;

}, '0.1.0', {
  requires: [
    'base',
    'cookie',
    'node',
    'juju-env-base',
    'juju-env-web-handler',
    'macaroon'
  ]
});
<commit_msg>Add unload event listener<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-2015 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';

/**
 * Bakery holds the context for making HTTP requests
 * that automatically acquire and discharge macaroons.
 *
 * @module env
 * @submodule env.bakery
 */

YUI.add('juju-env-bakery', function(Y) {

  var module = Y.namespace('juju.environments.web');
  var macaroon = Y.namespace('macaroon');

  /**
   * Bakery client inspired by the equivalent GO code.
   *
   * This object exposes the ability to perform requests
   * that automatically acquire and discharge macaroons
   *
   * @class Bakery
   */

  var Bakery = Y.Base.create('Bakery',
      Y.Base, [], {

      /**
       Initialize.

       @method initializer
       @param {Object} cfg A config object providing webhandler and visit method
           information. A visitMethod can be provided, which becomes the
           bakery's visitMethod. Alternatively, the bakery can be configured to
           non interactive mode. If neither is true, the default method is used.
           {visitMethod: fn, interactive: boolean,
            webhandler: obj, serviceName: string}
       @return {undefined} Nothing.
       */
      initializer: function (cfg) {
        this.webhandler = cfg.webhandler;
        if (cfg.visitMethod) {
          this.visitMethod = cfg.visitMethod;
        } else if (cfg.interactive !== undefined && !cfg.interactive) {
          this.visitMethod = this._nonInteractiveVisitMethod;
        } else {
          this.visitMethod = this._defaultVisitMethod;
        }
        this.macaroonName = 'Macaroons-' + cfg.serviceName;
        this.setCookiePath = cfg.setCookiePath;
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 except 401/407 where it does authentication.
       */
      sendGetRequest: function (path, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {'Bakery-Protocol-Version': 1};
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendGetRequest(
          path, headers, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this,
            path,
            successCallback,
            failureCallback
          )
        );
      },

      /**
       Takes the path supplied by the caller and makes a get request to the
       requestHandlerWithInteraction instance. If setCookiePath is set then
       it is used to set a cookie back to the ui after authentication.

       @param path {String} The path to make the api request to.
       @param data {String} Stringified JSON of parameters to send to the POST
           endpoint.
       @param successCallback {Function} Called when the api request completes
           successfully.
       @param failureCallback {Function} Called when the api request fails
           with a response of >= 400 except 401/407 where it does
           authentication.
       */
      sendPostRequest: function (path, data, successCallback, failureCallback) {
        var macaroons = this._getMacaroon();
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-type': 'application/json'
        };
        if (macaroons !== null) {
          headers['Macaroons'] = macaroons;
        }
        this.webhandler.sendPostRequest(
          path, headers, data, null, null, false, null,
          this._requestHandlerWithInteraction.bind(
            this, path, successCallback, failureCallback)
        );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback. For 407/401
       response it will request authentication through the macaroon provided in
       the 401/407 response.

       @method _requestHandlerWithInteraction
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       @param {Object} response The XHR response object.
       */
      _requestHandlerWithInteraction: function (path, successCallback,
                                                failureCallback, response) {
        var target = response.target;
        if (target.status === 401 &&
          target.getResponseHeader('Www-Authenticate') === 'Macaroon') {
          var jsonResponse = JSON.parse(target.responseText);
          this._authenticate(
            jsonResponse.Info.Macaroon,
            this._sendOriginalRequest.bind(
              this, path, successCallback, failureCallback
            ),
            failureCallback
          );
        } else {
          this._requestHandler(successCallback, failureCallback, response);
        }
      },

      /**
       Used to resend the original request without any interaction this time..

       @method _sendOriginalRequest
       @param {String} The path to make the api request to.
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400 (except 401/407).
       */
      _sendOriginalRequest: function(path, successCallback, failureCallback) {
          var macaroons = this._getMacaroon();
          var headers = {'Bakery-Protocol-Version': 1};
          if (macaroons !== null) {
            headers['Macaroons'] = macaroons;
          }
          this.webhandler.sendGetRequest(
            path, headers, null, null, false, null,
            this._requestHandler.bind(
              this, successCallback, failureCallback
            )
          );
      },

      /**
       Handles the request response from the _makeRequest method, calling the
       supplied failure callback if the response status was >= 400 or passing
       the response object to the supplied success callback.

       @method _requestHandler
       @param {Function} successCallback Called when the api request completes
       successfully.
       @param {Function} failureCallback Called when the api request fails
       with a response of >= 400.
       @param {Object} response The XHR response object.
       */
      _requestHandler: function (successCallback, failureCallback, response) {
        var target = response.target;
        if (target.status >= 400) {
          failureCallback(response);
          return;
        }
        successCallback(response);
      },

      /**
       Authenticate by discharging the macaroon and
       then set the cookie by calling the authCookiePath provided

       @method authenticate
       @param {Macaroon} The macaroon to be discharged
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _authenticate: function (m, requestFunction, failureCallback) {
        try {
          macaroon.discharge(
            macaroon.import(m),
            this._obtainThirdPartyDischarge.bind(this),
            this._processDischarges.bind(
              this,
              requestFunction,
              failureCallback
            ),
            failureCallback
          );
        } catch (ex) {
          failureCallback(ex.message);
        }
      },

      /**
       Process the discharged macaroon and call the end point to be able to set
       a cookie for the origin domain only when an auth cookie path is
       provided, then call the original function.

       @method _processDischarges
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       @param {[Macaroon]} The macaroons being discharged
       */
      _processDischarges: function (requestFunction, failureCallback,
                                    discharges) {
        var jsonMacaroon;
        try {
          jsonMacaroon = macaroon.export(discharges);
        } catch (ex) {
          failureCallback(ex.message);
        }
        var content = JSON.stringify({'Macaroons': jsonMacaroon});
        if (this.setCookiePath === undefined) {
          this._setMacaroonsCookie(requestFunction, jsonMacaroon);
          return;
        }
        this.webhandler.sendPutRequest(
          this.setCookiePath,
          null, content, null, null, true, null,
          this._requestHandler.bind(
            this,
            this._setMacaroonsCookie.bind(this, requestFunction, jsonMacaroon),
            failureCallback
          )
        );
      },

      /**
       Process successful discharge by setting Macaroons Cookie
       and invoke the original request

       @method _setMacaroonsCookie
       @param {Function} The path where to send put request
              to set the cookie back
       @param {Object} an exported Macaroon
       */
      _setMacaroonsCookie: function (originalRequest, jsonMacaroon) {
        var prefix = this.macaroonName + '=';
        document.cookie = prefix + btoa(JSON.stringify(jsonMacaroon));
        window.addEventListener('unload', function() {
          document.cookie = prefix + ';'
        });
        originalRequest();
      },

      /**
       Go to the discharge endpoint to obtain the third party discharge.

       @method obtainThirdPartyDischarge
       @param {String} The origin location
       @param {String} The third party location where to discharge
       @param {Function} The macaroon to be discharge
       @param {Function} The request to be sent again in case of
       successful authentication
       @param {Function} The callback failure in case of wrong authentication
       */
      _obtainThirdPartyDischarge: function (location,
                                            thirdPartyLocation, condition,
                                            successCallback, failureCallback) {
        thirdPartyLocation += '/discharge';
        var headers = {
          'Bakery-Protocol-Version': 1,
          'Content-Type': 'application/x-www-form-urlencoded'
        };
        var content = 'id=' + encodeURI(condition) +
          '&location=' + encodeURI(location);
        this.webhandler.sendPostRequest(
          thirdPartyLocation,
          headers, content, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            this._interact.bind(this, successCallback, failureCallback)
          )
        );
      },

    /**
      Get a JSON response from authentication either trusted or with
      interaction that contains a macaroon.

      @method _exportMacaroon
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
      @param {Object} response The XHR response object.
    */
    _exportMacaroon: function (successCallback, failureCallback, response) {
      try {
        var dm = macaroon.import(
          JSON.parse(response.target.responseText).Macaroon
        );
        successCallback(dm);
      } catch (ex) {
        failureCallback(ex.message);
      }
    },

    /**
      Interact to be able to sign-in to get authenticated.

      @method _interact
      @param {Function} The callback function to be sent in case of
                        successful authentication
      @param {Function} The callback function failure in case of
                        wrong authentication
    */
    _interact: function(successCallback, failureCallback, e) {
      var response = JSON.parse(e.target.responseText);
      if (response.Code !== 'interaction required') {
        failureCallback(response.Code);
        return;
      }

      this.visitMethod(response);

      this.webhandler.sendGetRequest(
          response.Info.WaitURL,
          null, null, null, false, null,
          this._requestHandler.bind(
            this,
            this._exportMacaroon.bind(this, successCallback, failureCallback),
            failureCallback
          )
      );
    },

    /**
      Non interactive visit method which sends the jujugui "auth" blob
      to the IdM to login.

      @method _nonInteractiveVisitMethod
      @param {Object} response An xhr response object.
    */
    _nonInteractiveVisitMethod: function(response) {
      var acceptHeaders = {'Accept': 'application/json'};
      var contentHeaders = {'Content-Type': 'application/json'};
      var login = function(response) {
        var method = JSON.parse(response.target.responseText).jujugui;
        var data = JSON.stringify({login: window.juju_config.auth});
        this.webhandler.sendPostRequest(
            method, contentHeaders, data,
            null, null, false, null, null);
      };

      this.webhandler.sendGetRequest(
          response.Info.VisitURL,
          acceptHeaders, null, null, false, null, login.bind(this));
    },

    /**
      Default visit method which is to open a window to the
      response.Info.VisitURL.

      @method _defaultVisitMethod
      @param {Object} response An xhr response object.
    */
    _defaultVisitMethod: function(response) {
      window.open(response.Info.VisitURL, 'Login');
    },

    /**
      Get macaroon from local cookie.

      @method _getMacaroon
      @return {String} Macaroons that was set in local cookie.
    */
    _getMacaroon: function() {
      var name = this.macaroonName + '=';
      var ca = document.cookie.split(';');
      for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
      }
      return null;
    }
  });

  module.Bakery = Bakery;

}, '0.1.0', {
  requires: [
    'base',
    'cookie',
    'node',
    'juju-env-base',
    'juju-env-web-handler',
    'macaroon'
  ]
});
<|endoftext|>


<commit_before>// jspsych.js
//
//	Josh de Leeuw
//	Percepts and Concepts Lab, Indiana University
//
//
(function($) {
    jsPsych = (function() {

        //
        // public object
        //
        var core = {};

        //
        // private class variables
        //

        // options
        var opts = {};
        // exp structure
        var exp_blocks = [];
        // flow control
        var curr_block = 0;
        // everything loaded?
        var initialized = false;
        // target DOM element
        var DOM_target;
        // time that the experiment began
        var exp_start_time;
        // turk info
        var turk_info;

        //
        // public methods
        //

        // core.init creates the experiment and starts running it
        //		display_element is an HTML element (usually a <div>) that will display jsPsych content
        //		options is an object: {
        //			"experiment_structure": an array of blocks specifying the experiment
        //			"finish": function to execute when the experiment ends
        //		}
        //
        core.init = function(options) {

            // reset the key variables
            exp_blocks = [];
            opts = {};
            initialized = false;
            curr_block = 0;

            // check if there is a body element on the page
            var default_display_element = $('body');
            if (default_display_element.length === 0) {
                $(document.documentElement).append($('<body>'));
                default_display_element = $('body');
            }

            var defaults = {
                'display_element': default_display_element,
                'on_finish': function(data) {
                    return undefined;
                },
                'on_trial_start': function() {
                    return undefined;
                },
                'on_trial_finish': function() {
                    return undefined;
                },
                'on_data_update': function(data) {
                    return undefined;
                }
            };

            // import options
            opts = $.extend({}, defaults, options);

            // set target
            DOM_target = opts.display_element;

            // add CSS class to DOM_target
            DOM_target.addClass('jspsych-display-element');

            run();
        };

        // core.data returns all of the data objects for each block as an array
        //      where core.data[0] = data object from block 0, etc...
        // if flatten is true, then the hierarchical structure of the data
        // is removed and each array entry will be a single trial.

        core.data = function(flatten) {
            var all_data = [];
            for (var i = 0; i < exp_blocks.length; i++) {
                all_data[i] = exp_blocks[i].data;
            }

            if(flatten===true){
                all_data = flattenData(all_data);
            }

            return all_data;
        };

        // core.dataAsCSV returns a CSV string that contains all of the data
        //      append_data is an option map object that will append values
        //      to every row. for example, if append_data = {"subject": 4},
        //      then a column called subject will be added to the data and
        //      it will always have the value 4.
        core.dataAsCSV = function(append_data) {
            var dataObj = core.data();
            return JSON2CSV(flattenData(dataObj, append_data));
        };

        core.saveCSVdata = function(filename, append_data) {
            var data_string = core.dataAsCSV(append_data);
            saveTextToFile(data_string, filename);
        };

        // core.progress returns an object with the following properties
        // 		total_blocks: the number of total blocks in the experiment
        //		total_trials: the number of total trials in the experiment
        //		current_trial_global: the current trial number in global terms
        // 					i.e. if each block has 20 trials and the experiment is
        //					currently in block 2 trial 10, this has a value of 30.
        //		current_trial_local: the current trial number within the block.
        //		current_block: the current block number.

        core.progress = function() {

            var total_trials = 0;
            for (var i = 0; i < exp_blocks.length; i++) {
                total_trials += exp_blocks[i].num_trials;
            }

            var current_trial_global = 0;
            for (var i = 0; i < curr_block; i++) {
                current_trial_global += exp_blocks[i].num_trials;
            }
            current_trial_global += exp_blocks[curr_block].trial_idx;

            var obj = {
                "total_blocks": exp_blocks.length,
                "total_trials": total_trials,
                "current_trial_global": current_trial_global,
                "current_trial_local": exp_blocks[curr_block].trial_idx,
                "current_block": curr_block
            };

            return obj;
        };

        // core.startTime() returns the Date object which represents the time that the experiment started.

        core.startTime = function() {
            return exp_start_time;
        };

        // core.preloadImage will load images into the browser cache so that they appear quickly when
        // used during a trial.
        //  images: array of paths to images
        //  callback_complete: a function with no arguments that calls when loading is complete
        //  callback_load: a function with a single argument that calls whenever an image is loaded
        //                  argument is the number of images currently loaded.

        core.preloadImages = function(images, callback_complete, callback_load){

            // flatten the images array
            images = flatten(images);

            var n_loaded = 0;
            var loadfn = (typeof callback_load === 'undefined') ? function(){} : callback_load;
            var finishfn = (typeof callback_complete === 'undefined') ? function(){} : callback_complete;

            for(var i=0;i<images.length;i++){
              var img = new Image();

              img.onload = function(){
                n_loaded++;
                loadfn(n_loaded);
                if(n_loaded == images.length){
                    finishfn();
                }
              };

              img.src = images[i];
            }
        };

        // core.turkInfo gets information relevant to mechanical turk experiments. returns an object
        // containing the workerID, assignmentID, and hitID, and whether or not the HIT is in
        // preview mode, meaning that they haven't accepted the HIT yet.
        core.turkInfo = function(force_refresh)
        {
            // default value is false
            force_refresh = (typeof force_refresh === 'undefined') ? false : force_refresh;
            // if we already have the turk_info and force_refresh is false
            // then just return the cached version.
            if(typeof turk_info !== 'undefined' && !force_refresh) {
                return turk_info;
            } else {

                var turk = {};

                var param = function(url, name ) {
                  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
                  var regexS = "[\\?&]"+name+"=([^&#]*)";
                  var regex = new RegExp( regexS );
                  var results = regex.exec( url );
                  return ( results == null ) ? "" : results[1];
                };

                var src = param(window.location.href, "assignmentId") ? window.location.href : document.referrer;

                var keys = ["assignmentId","hitId","workerId"];
                keys.map(
                    function(key) {
                        turk[key] = unescape(param(src, key));
                    }
                );

                turk.previewMode = (turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE");

                turk.outsideTurk = (!turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "")

                turk_info = turk;

                return turk;
            }

        };

        //
        // These are public functions, intended to be used for developing plugins.
        // They aren't considered part of the normal API for the core library.
        //

        core.normalizeTrialVariables = function(trial, protect){

            protect = (typeof protect === 'undefined') ? [] : protect;

            var keys = getKeys(trial);

            var tmp = {};
            for(var i=0; i<keys.length; i++){

                var process = true;
                for(var j = 0; j < protect.length; j++){
                    if(protect[j] == keys[i]){
                        process = false;
                        break;
                    }
                }

                if(typeof trial[keys[i]] == "function" && process){
                    tmp[keys[i]] = trial[keys[i]].call();
                } else {
                    tmp[keys[i]] = trial[keys[i]];
                }

            }

            return tmp;

        }

        // if possible_array is not an array, then return a one-element array
        // containing possible_array
        core.enforceArray = function(params, possible_arrays) {

            // function to check if something is an array, fallback
            // to string method if browser doesn't support Array.isArray
            var ckArray = Array.isArray || function(a) {
                return toString.call(a) == '[object Array]';
            }

            for(var i=0; i<possible_arrays.length; i++){
                params[possible_arrays[i]] = ckArray(params[possible_arrays[i]]) ? params[possible_arrays[i]] : [params[possible_arrays[i]]];
            }

            return params;
        }

        //
        // private functions //
        //
        function run() {
            // take the experiment structure and dynamically create a set of blocks
            exp_blocks = new Array(opts.experiment_structure.length);

            // iterate through block list to create trials
            for (var i = 0; i < exp_blocks.length; i++) {
                var trials = jsPsych[opts["experiment_structure"][i]["type"]]["create"].call(null, opts["experiment_structure"][i]);

                exp_blocks[i] = createBlock(trials);
            }

            // record the start time
            exp_start_time = new Date();

            // begin! - run the first block
            exp_blocks[0].next();
        }

        function nextBlock() {
            curr_block += 1;
            if (curr_block == exp_blocks.length) {
                finishExperiment();
            }
            else {
                exp_blocks[curr_block].next();
            }
        }

        function createBlock(trial_list) {
            var block = {
                trial_idx: -1,

                trials: trial_list,

                data: [],

                next: function() {

                    // call on_trial_finish()
                    //     if not very first trial
                    //		and not the last call in this block (no trial due to advance in block)
                    if (typeof this.trials[this.trial_idx + 1] != "undefined" && (curr_block != 0 || this.trial_idx > -1)) {
                        opts.on_trial_finish();
                    };

                    this.trial_idx = this.trial_idx + 1;

                    var curr_trial = this.trials[this.trial_idx];

                    if (typeof curr_trial == "undefined") {
                        return this.done();
                    }

                    // call on_trial_start()
                    opts.on_trial_start();

                    do_trial(this, curr_trial);
                },

                writeData: function(data_object) {
                    this.data[this.trial_idx] = data_object;
                    opts.on_data_update(data_object);
                },

                done: nextBlock,

                num_trials: trial_list.length
            };

            return block;
        }

        function finishExperiment() {
            opts["on_finish"].apply((new Object()), [core.data()]);
        }

        function do_trial(block, trial) {
            // execute trial method
            jsPsych[trial.type]["trial"].call(this, DOM_target, block, trial, 1);
        }

        //
        // A few helper functions to handle data format conversion
        //
        function flattenData(data_object, append_data) {

            append_data = (typeof append_data === undefined) ? {} : append_data;

            var trials = [];

            // loop through data_object
            for (var i = 0; i < data_object.length; i++) {
                for (var j = 0; j < data_object[i].length; j++) {
                    var data = $.extend({}, data_object[i][j], append_data);
                    trials.push(data);
                }
            }

            return trials;
        }

        // this function based on code suggested by StackOverflow users:
        // http://stackoverflow.com/users/64741/zachary
        // http://stackoverflow.com/users/317/joseph-sturtevant
        function JSON2CSV(objArray) {
            var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
            var line = '';
            var result = '';
            var columns = [];

            var i = 0;
            for (var j = 0; j < array.length; j++) {
                for (var key in array[j]) {
                    var keyString = key + "";
                    keyString = '"' + keyString.replace(/"/g, '""') + '",';
                    if ($.inArray(key,columns) == -1) {
                        columns[i] = key;
                        line += keyString;
                        i++;
                    }
                }
            }

            line = line.slice(0, - 1);
            result += line + '\r\n';

            for (var i = 0; i < array.length; i++) {
                var line = '';
                for (var j = 0; j < columns.length; j++) {
                    var value = (typeof array[i][columns[j]] === 'undefined') ? '' : array[i][columns[j]];
                    var valueString = value + "";
                    line += '"' + valueString.replace(/"/g, '""') + '",';
                }

                line = line.slice(0, - 1);
                result += line + '\r\n';
            }

            return result;
        }

        function saveTextToFile(textstr, filename) {
            var blobToSave = new Blob([textstr], {
                type: 'text/plain'
            });
            var blobURL = "";
            if (typeof window.webkitURL !== 'undefined') {
                blobURL = window.webkitURL.createObjectURL(blobToSave);
            }
            else {
                blobURL = window.URL.createObjectURL(blobToSave);
            }
            DOM_target.append($('<a>', {
                id: 'jspsych-download-as-text-link',
                href: blobURL,
                css: {
                    display: 'none'
                },
                download: filename,
                html: 'download file'
            }));
            $('#jspsych-download-as-text-link')[0].click();
        }

        function getKeys(obj) {
            var r = [];
            for (var k in obj) {
                if (!obj.hasOwnProperty(k))
                    continue;
                r.push(k);
            }
            return r;
        }

        // private function to flatten nested arrays
        function flatten(arr, out) {
            out = (typeof out === 'undefined') ? [] : out;
            for (var i = 0; i < arr.length; i++) {
                if (Array.isArray(arr[i])) {
                    flatten(arr[i], out);
                } else {
                    out.push(arr[i]);
                }
            }
            return out;
        }

        return core;
    })();
})(jQuery);
<commit_msg>add jsPsych.totalTime method<commit_after>// jspsych.js
//
//	Josh de Leeuw
//	Percepts and Concepts Lab, Indiana University
//
//
(function($) {
    jsPsych = (function() {

        //
        // public object
        //
        var core = {};

        //
        // private class variables
        //

        // options
        var opts = {};
        // exp structure
        var exp_blocks = [];
        // flow control
        var curr_block = 0;
        // everything loaded?
        var initialized = false;
        // target DOM element
        var DOM_target;
        // time that the experiment began
        var exp_start_time;
        // turk info
        var turk_info;

        //
        // public methods
        //

        // core.init creates the experiment and starts running it
        //		display_element is an HTML element (usually a <div>) that will display jsPsych content
        //		options is an object: {
        //			"experiment_structure": an array of blocks specifying the experiment
        //			"finish": function to execute when the experiment ends
        //		}
        //
        core.init = function(options) {

            // reset the key variables
            exp_blocks = [];
            opts = {};
            initialized = false;
            curr_block = 0;

            // check if there is a body element on the page
            var default_display_element = $('body');
            if (default_display_element.length === 0) {
                $(document.documentElement).append($('<body>'));
                default_display_element = $('body');
            }

            var defaults = {
                'display_element': default_display_element,
                'on_finish': function(data) {
                    return undefined;
                },
                'on_trial_start': function() {
                    return undefined;
                },
                'on_trial_finish': function() {
                    return undefined;
                },
                'on_data_update': function(data) {
                    return undefined;
                }
            };

            // import options
            opts = $.extend({}, defaults, options);

            // set target
            DOM_target = opts.display_element;

            // add CSS class to DOM_target
            DOM_target.addClass('jspsych-display-element');

            run();
        };

        // core.data returns all of the data objects for each block as an array
        //      where core.data[0] = data object from block 0, etc...
        // if flatten is true, then the hierarchical structure of the data
        // is removed and each array entry will be a single trial.

        core.data = function(flatten) {
            var all_data = [];
            for (var i = 0; i < exp_blocks.length; i++) {
                all_data[i] = exp_blocks[i].data;
            }

            if(flatten===true){
                all_data = flattenData(all_data);
            }

            return all_data;
        };

        // core.dataAsCSV returns a CSV string that contains all of the data
        //      append_data is an option map object that will append values
        //      to every row. for example, if append_data = {"subject": 4},
        //      then a column called subject will be added to the data and
        //      it will always have the value 4.
        core.dataAsCSV = function(append_data) {
            var dataObj = core.data();
            return JSON2CSV(flattenData(dataObj, append_data));
        };

        core.saveCSVdata = function(filename, append_data) {
            var data_string = core.dataAsCSV(append_data);
            saveTextToFile(data_string, filename);
        };

        // core.progress returns an object with the following properties
        // 		total_blocks: the number of total blocks in the experiment
        //		total_trials: the number of total trials in the experiment
        //		current_trial_global: the current trial number in global terms
        // 					i.e. if each block has 20 trials and the experiment is
        //					currently in block 2 trial 10, this has a value of 30.
        //		current_trial_local: the current trial number within the block.
        //		current_block: the current block number.

        core.progress = function() {

            var total_trials = 0;
            for (var i = 0; i < exp_blocks.length; i++) {
                total_trials += exp_blocks[i].num_trials;
            }

            var current_trial_global = 0;
            for (var i = 0; i < curr_block; i++) {
                current_trial_global += exp_blocks[i].num_trials;
            }
            current_trial_global += exp_blocks[curr_block].trial_idx;

            var obj = {
                "total_blocks": exp_blocks.length,
                "total_trials": total_trials,
                "current_trial_global": current_trial_global,
                "current_trial_local": exp_blocks[curr_block].trial_idx,
                "current_block": curr_block
            };

            return obj;
        };

        // core.startTime() returns the Date object which represents the time that the experiment started.

        core.startTime = function() {
            return exp_start_time;
        };

        // core.totalTime() returns the length of time in ms since the experiment began

        core.totalTime = function() {
            return (new Date()).getTime() - exp_start_time.getTime();
        }

        // core.preloadImage will load images into the browser cache so that they appear quickly when
        // used during a trial.
        //  images: array of paths to images
        //  callback_complete: a function with no arguments that calls when loading is complete
        //  callback_load: a function with a single argument that calls whenever an image is loaded
        //                  argument is the number of images currently loaded.

        core.preloadImages = function(images, callback_complete, callback_load){

            // flatten the images array
            images = flatten(images);

            var n_loaded = 0;
            var loadfn = (typeof callback_load === 'undefined') ? function(){} : callback_load;
            var finishfn = (typeof callback_complete === 'undefined') ? function(){} : callback_complete;

            for(var i=0;i<images.length;i++){
              var img = new Image();

              img.onload = function(){
                n_loaded++;
                loadfn(n_loaded);
                if(n_loaded == images.length){
                    finishfn();
                }
              };

              img.src = images[i];
            }
        };

        // core.turkInfo gets information relevant to mechanical turk experiments. returns an object
        // containing the workerID, assignmentID, and hitID, and whether or not the HIT is in
        // preview mode, meaning that they haven't accepted the HIT yet.
        core.turkInfo = function(force_refresh)
        {
            // default value is false
            force_refresh = (typeof force_refresh === 'undefined') ? false : force_refresh;
            // if we already have the turk_info and force_refresh is false
            // then just return the cached version.
            if(typeof turk_info !== 'undefined' && !force_refresh) {
                return turk_info;
            } else {

                var turk = {};

                var param = function(url, name ) {
                  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
                  var regexS = "[\\?&]"+name+"=([^&#]*)";
                  var regex = new RegExp( regexS );
                  var results = regex.exec( url );
                  return ( results == null ) ? "" : results[1];
                };

                var src = param(window.location.href, "assignmentId") ? window.location.href : document.referrer;

                var keys = ["assignmentId","hitId","workerId"];
                keys.map(
                    function(key) {
                        turk[key] = unescape(param(src, key));
                    }
                );

                turk.previewMode = (turk.assignmentId == "ASSIGNMENT_ID_NOT_AVAILABLE");

                turk.outsideTurk = (!turk.previewMode && turk.hitId === "" && turk.assignmentId == "" && turk.workerId == "")

                turk_info = turk;

                return turk;
            }

        };

        //
        // These are public functions, intended to be used for developing plugins.
        // They aren't considered part of the normal API for the core library.
        //

        core.normalizeTrialVariables = function(trial, protect){

            protect = (typeof protect === 'undefined') ? [] : protect;

            var keys = getKeys(trial);

            var tmp = {};
            for(var i=0; i<keys.length; i++){

                var process = true;
                for(var j = 0; j < protect.length; j++){
                    if(protect[j] == keys[i]){
                        process = false;
                        break;
                    }
                }

                if(typeof trial[keys[i]] == "function" && process){
                    tmp[keys[i]] = trial[keys[i]].call();
                } else {
                    tmp[keys[i]] = trial[keys[i]];
                }

            }

            return tmp;

        }

        // if possible_array is not an array, then return a one-element array
        // containing possible_array
        core.enforceArray = function(params, possible_arrays) {

            // function to check if something is an array, fallback
            // to string method if browser doesn't support Array.isArray
            var ckArray = Array.isArray || function(a) {
                return toString.call(a) == '[object Array]';
            }

            for(var i=0; i<possible_arrays.length; i++){
                params[possible_arrays[i]] = ckArray(params[possible_arrays[i]]) ? params[possible_arrays[i]] : [params[possible_arrays[i]]];
            }

            return params;
        }

        //
        // private functions //
        //
        function run() {
            // take the experiment structure and dynamically create a set of blocks
            exp_blocks = new Array(opts.experiment_structure.length);

            // iterate through block list to create trials
            for (var i = 0; i < exp_blocks.length; i++) {
                var trials = jsPsych[opts["experiment_structure"][i]["type"]]["create"].call(null, opts["experiment_structure"][i]);

                exp_blocks[i] = createBlock(trials);
            }

            // record the start time
            exp_start_time = new Date();

            // begin! - run the first block
            exp_blocks[0].next();
        }

        function nextBlock() {
            curr_block += 1;
            if (curr_block == exp_blocks.length) {
                finishExperiment();
            }
            else {
                exp_blocks[curr_block].next();
            }
        }

        function createBlock(trial_list) {
            var block = {
                trial_idx: -1,

                trials: trial_list,

                data: [],

                next: function() {

                    // call on_trial_finish()
                    //     if not very first trial
                    //		and not the last call in this block (no trial due to advance in block)
                    if (typeof this.trials[this.trial_idx + 1] != "undefined" && (curr_block != 0 || this.trial_idx > -1)) {
                        opts.on_trial_finish();
                    };

                    this.trial_idx = this.trial_idx + 1;

                    var curr_trial = this.trials[this.trial_idx];

                    if (typeof curr_trial == "undefined") {
                        return this.done();
                    }

                    // call on_trial_start()
                    opts.on_trial_start();

                    do_trial(this, curr_trial);
                },

                writeData: function(data_object) {
                    this.data[this.trial_idx] = data_object;
                    opts.on_data_update(data_object);
                },

                done: nextBlock,

                num_trials: trial_list.length
            };

            return block;
        }

        function finishExperiment() {
            opts["on_finish"].apply((new Object()), [core.data()]);
        }

        function do_trial(block, trial) {
            // execute trial method
            jsPsych[trial.type]["trial"].call(this, DOM_target, block, trial, 1);
        }

        //
        // A few helper functions to handle data format conversion
        //
        function flattenData(data_object, append_data) {

            append_data = (typeof append_data === undefined) ? {} : append_data;

            var trials = [];

            // loop through data_object
            for (var i = 0; i < data_object.length; i++) {
                for (var j = 0; j < data_object[i].length; j++) {
                    var data = $.extend({}, data_object[i][j], append_data);
                    trials.push(data);
                }
            }

            return trials;
        }

        // this function based on code suggested by StackOverflow users:
        // http://stackoverflow.com/users/64741/zachary
        // http://stackoverflow.com/users/317/joseph-sturtevant
        function JSON2CSV(objArray) {
            var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
            var line = '';
            var result = '';
            var columns = [];

            var i = 0;
            for (var j = 0; j < array.length; j++) {
                for (var key in array[j]) {
                    var keyString = key + "";
                    keyString = '"' + keyString.replace(/"/g, '""') + '",';
                    if ($.inArray(key,columns) == -1) {
                        columns[i] = key;
                        line += keyString;
                        i++;
                    }
                }
            }

            line = line.slice(0, - 1);
            result += line + '\r\n';

            for (var i = 0; i < array.length; i++) {
                var line = '';
                for (var j = 0; j < columns.length; j++) {
                    var value = (typeof array[i][columns[j]] === 'undefined') ? '' : array[i][columns[j]];
                    var valueString = value + "";
                    line += '"' + valueString.replace(/"/g, '""') + '",';
                }

                line = line.slice(0, - 1);
                result += line + '\r\n';
            }

            return result;
        }

        function saveTextToFile(textstr, filename) {
            var blobToSave = new Blob([textstr], {
                type: 'text/plain'
            });
            var blobURL = "";
            if (typeof window.webkitURL !== 'undefined') {
                blobURL = window.webkitURL.createObjectURL(blobToSave);
            }
            else {
                blobURL = window.URL.createObjectURL(blobToSave);
            }
            DOM_target.append($('<a>', {
                id: 'jspsych-download-as-text-link',
                href: blobURL,
                css: {
                    display: 'none'
                },
                download: filename,
                html: 'download file'
            }));
            $('#jspsych-download-as-text-link')[0].click();
        }

        function getKeys(obj) {
            var r = [];
            for (var k in obj) {
                if (!obj.hasOwnProperty(k))
                    continue;
                r.push(k);
            }
            return r;
        }

        // private function to flatten nested arrays
        function flatten(arr, out) {
            out = (typeof out === 'undefined') ? [] : out;
            for (var i = 0; i < arr.length; i++) {
                if (Array.isArray(arr[i])) {
                    flatten(arr[i], out);
                } else {
                    out.push(arr[i]);
                }
            }
            return out;
        }

        return core;
    })();
})(jQuery);
<|endoftext|>


<commit_before>(function(module) {
  "use strict";
  var anon = {},
    settings = {},
    banList = [],
    regexPhrase = /\[\[ANON\]\]/,
    breakLine = '\r\n\r\n',
    signTitle = '###### sign: ',
    winston = module.parent.require('winston'),
    meta = module.parent.require('./meta'),
    crypto = require('crypto');

  anon.init = function(params, callback) {
    var router = params.router,
      hostMiddleware = params.middleware;

    router.get('/admin/plugins/anon', hostMiddleware.admin.buildHeader, render);
    router.get('/api/admin/plugins/anon', render);

    loadSettings()
      .then(function() {
        winston.info('[nodebb-plugin-anon] Settings loaded');
      });

    loadBanList()
      .then(function() {
        winston.info('[nodebb-plugin-anon] Ban list loaded');
      });

    callback();
  }

  anon.filter = function(data, callback) {
    if (settings != null) {
      if (regexPhrase.test(data.content)) {
        var sign = getSign(data.uid.toString());

        if (existInBanList(sign) && hasAccessCategory(data.cid)) {
          return callback(new Error('[[error:no-privileges]]'))
        }

        data.content = data.content.replace(regexPhrase, '') + breakLine + signTitle + sign;
        data.uid = settings.uid;
      }
    }

    return callback(null, data);
  }

  anon.addAdminNavigation = function(header, callback) {
    header.plugins.push({
      route: '/plugins/anon',
      icon: 'fa-tint',
      name: 'Anon'
    });

    callback(null, header);
  }

  function hasAccessCategory(cid) {
    var accessArrayCategory = settings.accessCategory.splice(', ');
    return accessArrayCategory.indexOf(cid);
  }

  function existInBanList(sign) {
    return banList.indexOf(sign) === -1;
  }

  function getSign(uid) {
    return crypto.createHash('sha256').update(uid + settings.salt).digest('base64');
  }

  function render(req, res) {
    res.render('admin/plugins/anon', {});
  }

  function saveSettings() {
    return new Promise(function(res, err) {
      meta.settings.set('anon', settings, function(err, data) {
        res();
      });
    });
  }

  function loadSettings() {
    return new Promise(function(res, err) {
      meta.settings.get('anon', function(err, data) {
        settings = data;
        res();
      });
    });
  }

  function loadBanList() {
    return new Promise(function(res, err) {
      meta.settings.get('anon-banlist', function(err, data) {
        banList = data;
        res();
      });
    });
  }

  module.exports = anon;
}(module));<commit_msg>Add more debug logging<commit_after>(function(module) {
  "use strict";
  var anon = {},
    settings = {},
    banList = [],
    regexPhrase = /\[\[ANON\]\]/,
    breakLine = '\r\n\r\n',
    signTitle = '###### sign: ',
    winston = module.parent.require('winston'),
    meta = module.parent.require('./meta'),
    crypto = require('crypto');

  anon.init = function(params, callback) {
    var router = params.router,
      hostMiddleware = params.middleware;

    router.get('/admin/plugins/anon', hostMiddleware.admin.buildHeader, render);
    router.get('/api/admin/plugins/anon', render);

    loadSettings()
      .then(function() {
        winston.info('[nodebb-plugin-anon] Settings loaded');
      });

    loadBanList()
      .then(function() {
        winston.info('[nodebb-plugin-anon] Ban list loaded');
      });

    callback();
  }

  anon.filter = function(data, callback) {
    if (settings != null) {
      if (regexPhrase.test(data.content)) {
        var sign = getSign(data.uid.toString());

        if (existInBanList(sign) && hasAccessCategory(data.cid)) {
          return callback(new Error('[[error:no-privileges]]'))
        }

        data.content = data.content.replace(regexPhrase, '') + breakLine + signTitle + sign;
        data.uid = settings.uid;
      }
    }

    return callback(null, data);
  }

  anon.addAdminNavigation = function(header, callback) {
    header.plugins.push({
      route: '/plugins/anon',
      icon: 'fa-tint',
      name: 'Anon'
    });

    callback(null, header);
  }

  function hasAccessCategory(cid) {
    var accessArrayCategory = settings.accessCategory.splice(', ');
    return accessArrayCategory.indexOf(cid);
  }

  function existInBanList(sign) {
    return banList.indexOf(sign) === -1;
  }

  function getSign(uid) {
    return crypto.createHash('sha256').update(uid + settings.salt).digest('base64');
  }

  function render(req, res) {
    res.render('admin/plugins/anon', {});
  }

  function saveSettings() {
    return new Promise(function(res, err) {
      meta.settings.set('anon', settings, function(err, data) {
        res();
      });
    });
  }

  function loadSettings() {
    return new Promise(function(res, err) {
      meta.settings.get('anon', function(err, data) {
        settings = data;
        console.log(settings);
        res();
      });
    });
  }

  function loadBanList() {
    return new Promise(function(res, err) {
      meta.settings.get('anon-banlist', function(err, data) {
        banList = data;
        console.log(banList);
        res();
      });
    });
  }

  module.exports = anon;
}(module));<|endoftext|>