<commit_before>'use strict';
(function(module) {
  try {
    module = angular.module('tink.nationalnumber');
  } catch (e) {
    module = angular.module('tink.nationalnumber', ['tink.safeApply','tink.formathelper']);
  }
  module.directive('tinkNationalNumber',['$window','safeApply',function($window,safeApply){
     return {
      restrict:'AE',
      controller:'tinkFormatController',
      controllerAs:'ctrl',
      require:['tinkNationalNumber','ngModel','?^form'],
      scope:{
        isDisabled:'=?'
      },
      template: function() {
        var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
        var isTouch = ('createTouch' in $window.document) && isNative;
        if (isTouch) {
          return '
<input class="hide-styling" type="text">
';
        } else {
          return '

{{placeholder}}

';
        }
      },
      link:function(scope,elm,attr,ctrl){
        var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
        var isTouch = ('createTouch' in $window.document) && isNative;
        var controller = ctrl[0];
        var form = ctrl[2];
        var ngControl = ctrl[1];
        var element = elm.find('div>:first');
        //variable
        var config = {
          format: '00.00.00-000.00',
          placeholder: 'xx.xx.xx-xxx.xx'
        };

         ngControl.$parsers.unshift(function(value) {
          checkvalidty(value);
          return value;
         });

         ngControl.$formatters.push(function(modelValue) {
          if(modelValue !== undefined){
            if(modelValue && modelValue.length === 11){
              modelValue = modelValue.substr(0,2) + '.' + modelValue.substr(2,2)+ '.' + modelValue.substr(4,2)+'-'+ modelValue.substr(6,3)+'-'+modelValue.substr(9,2);
            }

            if(validFormat(modelValue)){
              if(isTouch){
                element.val(modelValue);
              }else{
                controller.setValue(modelValue,null);
              }
            }else{
              modelValue = null;
              ngControl.$setViewValue(modelValue);
            }
            checkvalidty(modelValue);

          }
          return modelValue;
         });

         element.unbind('input').unbind('keydown').unbind('change');
        //on blur update the model.
        element.on('blur', function() {
          safeApply(scope,function(){
            var value;
            if (isTouch) {
              value = element.val();
            }else{
              value = controller.getValue();
            }

              if(isRRNoValid(value)){
                ngControl.$setViewValue(value);
                ngControl.$render();
              }else{
                 ngControl.$setViewValue(null);
              }
              checkvalidty(value);
              /*if(value === 'xx.xx.xx-xxx.xx' || value === ''){
                ngControl.$setViewValue(null);
              }*/
          });
        });

         var isRequired = (function(){
            if(attr.required === 'true' || attr.required === '' || attr.required === 'required'){
              return true;
            }else{
              return false;
            }
          })();

          function validFormat(value){
            if(value && value.length === 11){
              return value.match(/[0-9]*/g);
            }else if(value && value.length === 15){
              return value.match(/[0-9][0-9].[0-9][0-9].[0-9][0-9]-[0-9][0-9][0-9].[0-9][0-9]/g);
            }else{
              return false;
            }
          }

         function isRRNoValid(n) {
          if(typeof n !== 'string'){
            return false;
          }
            n = n.replace(/[^\d]*/g, '');
              // RR numbers need to be 11 chars long
              if (n.length !== 11) {
                return false;
              }

              var checkDigit = n.substr(n.length - 2, 2);
              var modFunction = function(nr) { return 97 - (nr % 97); };
              var nrToCheck = parseInt(n.substr(0, 9));

              // first check without 2
              if (modFunction(nrToCheck) === parseInt(checkDigit)) {
                return true;
              }
              // then check with 2 appended for y2k+ births
              nrToCheck = parseInt('2' + n.substr(0, 9));
              return (modFunction(nrToCheck) === parseInt(checkDigit));
          }

         function checkvalidty(value){
          if(value === config.placeholder || value === '' || value === null || value === undefined){
            ngControl.$setValidity('format',true);
          }else{
            ngControl.$setValidity('format',isRRNoValid(value));
          }

          if(isRequired){
            if(controller.getValue() === config.placeholder || value === '' || value === null || value === undefined){
              ngControl.$setValidity('required',false);
              ngControl.$setValidity('format',true);
            }else{
              ngControl.$setValidity('required',true);
            }
          }
         }

         if (!isTouch) {
            controller.init(element,config,form,ngControl);
         }
      }
    };
  }]);
})();;<commit_msg>update fieldset<commit_after>'use strict';
(function(module) {
  try {
    module = angular.module('tink.nationalnumber');
  } catch (e) {
    module = angular.module('tink.nationalnumber', ['tink.safeApply','tink.formathelper']);
  }
  module.directive('tinkNationalNumber',['$window','safeApply','$timeout',function($window,safeApply,$setimeout){
     return {
      restrict:'AE',
      controller:'tinkFormatController',
      controllerAs:'ctrl',
      require:['tinkNationalNumber','ngModel','?^form'],
      scope:{
        isDisabled:'=?',
        ngModel:'='
      },
      template: function() {
        var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
        var isTouch = ('createTouch' in $window.document) && isNative;
        if (isTouch) {
          return '
<input class="hide-styling" type="text">
';
        } else {
          return '

{{placeholder}}

';
        }
      },
      link:function(scope,elm,attr,ctrl){
        var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
        var isTouch = ('createTouch' in $window.document) && isNative;
        var controller = ctrl[0];
        var form = ctrl[2];
        var ngControl = ctrl[1];
        var element = elm.find('div>:first');
        //variable
        var config = {
          format: '00.00.00-000.00',
          placeholder: 'xx.xx.xx-xxx.xx'
        };

         ngControl.$parsers.unshift(function(value) {
          checkvalidty(value);
          return value;
         });

         ngControl.$formatters.push(function(modelValue) {
          if(modelValue !== undefined){
            if(modelValue && modelValue.length === 11){
              modelValue = modelValue.substr(0,2) + '.' + modelValue.substr(2,2)+ '.' + modelValue.substr(4,2)+'-'+ modelValue.substr(6,3)+'-'+modelValue.substr(9,2);
            }

            if(validFormat(modelValue)){
              if(isTouch){
                element.val(modelValue);
              }else{
                controller.setValue(modelValue,null);
              }
            }else{
              modelValue = null;
              ngControl.$setViewValue(modelValue);
            }
            checkvalidty(modelValue);

          }
          return modelValue;
         });

         element.unbind('input').unbind('keydown').unbind('change');
        //on blur update the model.
        element.on('blur', function() {
          safeApply(scope,function(){
            var value;
            if (isTouch) {
              value = element.val();
            }else{
              value = controller.getValue();
            }

              if(isRRNoValid(value)){
                ngControl.$setViewValue(value);
                ngControl.$render();
                //ngControl.$setPristine();
              }else{
                 ngControl.$setViewValue();
                // scope.ngModel = null;
                 //ngControl.$setPristine(true);
              }
              checkvalidty(value);
              /*if(value === 'xx.xx.xx-xxx.xx' || value === ''){
                ngControl.$setViewValue(null);
              }*/
          });
        });

        elm.bind('focus',function(){
          element.focus();
        });

        var prevValue;
        element.bind('valueChanged', function (e, val) {
            //We put this in a safeaply because we are out of the angular scope !
            safeApply(scope, function () {
              //Check if the date we received is a valid date.
              if(ctrl[1] && prevValue !== val){
                ctrl[1].$setDirty();
              }
              prevValue = val;
            });
          });

         var isRequired = (function(){
            if(attr.required === 'true' || attr.required === '' || attr.required === 'required'){
              return true;
            }else{
              return false;
            }
          })();

          function validFormat(value){
            if(value && value.length === 11){
              return value.match(/[0-9]*/g);
            }else if(value && value.length === 15){
              return value.match(/[0-9][0-9].[0-9][0-9].[0-9][0-9]-[0-9][0-9][0-9].[0-9][0-9]/g);
            }else{
              return false;
            }
          }

         function isRRNoValid(n) {
          if(typeof n !== 'string'){
            return false;
          }
            n = n.replace(/[^\d]*/g, '');
              // RR numbers need to be 11 chars long
              if (n.length !== 11) {
                return false;
              }

              var checkDigit = n.substr(n.length - 2, 2);
              var modFunction = function(nr) { return 97 - (nr % 97); };
              var nrToCheck = parseInt(n.substr(0, 9));

              // first check without 2
              if (modFunction(nrToCheck) === parseInt(checkDigit)) {
                return true;
              }
              // then check with 2 appended for y2k+ births
              nrToCheck = parseInt('2' + n.substr(0, 9));
              return (modFunction(nrToCheck) === parseInt(checkDigit));
          }

         function checkvalidty(value){
          if(value === config.placeholder || value === '' || value === null || value === undefined){
            ngControl.$setValidity('format',true);
          }else{
            ngControl.$setValidity('format',isRRNoValid(value));
          }

          if(isRequired){
            if(controller.getValue() === config.placeholder || value === '' || value === null || value === undefined){
              ngControl.$setValidity('required',false);
              ngControl.$setValidity('format',true);
            }else{
              ngControl.$setValidity('required',true);
            }
          }
         }

         if (!isTouch) {
            controller.init(element,config,form,ngControl);
         }
      }
    };
  }]);
})();<|endoftext|>


<commit_before>/*
Use this to create beautiful things like:
  carnival-joining perverted grapes
  stripped effervescent shart
  acrid haunted furry
  pulsing confused fetish
  purified possibly-ambiguous cheese-fingers
  drunken stripped backdoor
  swollen carnival-joining backdoor
  haunted effervescent twerk
  stripped hairless clown
*/

const adjectives = [
  'infeasible',
  'possibly-ambiguous',
  'effervescent',
  'cucumberish',
  'gesticulating',
  'swollen',
  'bulging',
  'bifurcated',
  'overused',
  'bacterial',
  'carnival-joining',
  'drunken',
  'weathered',
  'smelly',
  'pulsing',
  'tangled',
  'confused',
  'fecal',
  'perverted',
  'stripped',
  'acrid',
  'haunted',
  'purified',
  'flapping',
  'hairless',
  'weaponized',
  'crusty',
  'drooling,
  'gunktastic'
]

const nouns = [
  'hickey',
  'grapes',
  'eggplant',
  'shart',
  'twerk',
  'restraining order',
  'sniff-weasel',
  'vomit',
  'furry',
  'backdoor',
  'cheese-fingers',
  'clown',
  'wizard',
  'fetish',
  'muffin-top',
  'hissy-fit',
  'hot dog',
  'stink-cave'
]

const onlyUnique = (value, index, self) => {
  return self.indexOf(value) === index
}

const sampleSet = (items, quantity = 1) => {
  let data = []
  while (data.length < quantity) {
    const rando = items[Math.floor(Math.random()*items.length)]
    data.push(rando)
    data = data.filter(onlyUnique)
  }
  return data
}

// TADAAAAAAA
export default (joiner = ' ') => sampleSet(adjectives, 2).concat(sampleSet(nouns)).join(joiner)
<commit_msg>typo fix :rage:<commit_after>/*
Use this to create beautiful things like:
  carnival-joining perverted grapes
  stripped effervescent shart
  acrid haunted furry
  pulsing confused fetish
  purified possibly-ambiguous cheese-fingers
  drunken stripped backdoor
  swollen carnival-joining backdoor
  haunted effervescent twerk
  stripped hairless clown
*/

const adjectives = [
  'infeasible',
  'possibly-ambiguous',
  'effervescent',
  'cucumberish',
  'gesticulating',
  'swollen',
  'bulging',
  'bifurcated',
  'overused',
  'bacterial',
  'carnival-joining',
  'drunken',
  'weathered',
  'smelly',
  'pulsing',
  'tangled',
  'confused',
  'fecal',
  'perverted',
  'stripped',
  'acrid',
  'haunted',
  'purified',
  'flapping',
  'hairless',
  'weaponized',
  'crusty',
  'drooling',
  'gunktastic'
]

const nouns = [
  'hickey',
  'grapes',
  'eggplant',
  'shart',
  'twerk',
  'restraining order',
  'sniff-weasel',
  'vomit',
  'furry',
  'backdoor',
  'cheese-fingers',
  'clown',
  'wizard',
  'fetish',
  'muffin-top',
  'hissy-fit',
  'hot dog',
  'stink-cave'
]

const onlyUnique = (value, index, self) => {
  return self.indexOf(value) === index
}

const sampleSet = (items, quantity = 1) => {
  let data = []
  while (data.length < quantity) {
    const rando = items[Math.floor(Math.random()*items.length)]
    data.push(rando)
    data = data.filter(onlyUnique)
  }
  return data
}

// TADAAAAAAA
export default (joiner = ' ') => sampleSet(adjectives, 2).concat(sampleSet(nouns)).join(joiner)
<|endoftext|>


<commit_before>/*global Reply, Topic, Gambit */
/**

  Topics are a grouping of gambits.
  The order of the Gambits are important, and a gambit can live in more than one topic.

**/

var natural = require("natural");
var _ = require("lodash");
var async = require("async");
var findOrCreate = require("mongoose-findorcreate");
var debug = require("debug")("Topics");
var Sort = require("./sort");
var Common = require("./common");
var safeEval = require("safe-eval");

var TfIdf = natural.TfIdf;
var tfidf = new TfIdf();

module.exports = function (mongoose) {

  natural.PorterStemmer.attach();

  var topicSchema = new mongoose.Schema({
    name: {type: String, index: true, unique: true},
    keep: {type: Boolean, default: false },
    system: {type: Boolean, default: false},
    filter: {type: String, default: ""},
    keywords: {type: Array},
    gambits: [{ type: String, ref: "Gambit"}],
    conditions: [{ type: String, ref: "Condition"}]
  });

  topicSchema.pre("save", function (next) {
    var self = this;
    var kw;

    if (!_.isEmpty(this.keywords)) {
      kw = self.keywords.join(" ");
      if (kw) {
        tfidf.addDocument(kw.tokenizeAndStem(), self.name);
      }
    }
    next();
  });

  topicSchema.methods.createCondition = function(conditionData, callback) {
    var self = this;
    if (!conditionData) {
      return callback("No data");
    }

    if (_.isEmpty(conditionData.gambits)) {
      return callback("No gambits");
    } else {
      var gambits = [];
      var gambitLookup = function(gambit_id, next) {
        Gambit.findOne({id: gambit_id}, function(error, gambit) {
          gambits.push(gambit._id);
          next();
        });
      };

      async.each(conditionData.gambits, gambitLookup, function() {

        conditionData.gambits = gambits;
        var condition = new Condition(conditionData);

        condition.save(function (err) {
          if (err) {
            return callback(err);
          }
          self.conditions.addToSet(condition._id);
          self.save(function (err2) {
            callback(err2, condition);
          });
        });
      })
    }
  };

  // This will create the Gambit and add it to the model
  topicSchema.methods.createGambit = function (gambitData, callback) {
    if (!gambitData) {
      return callback("No data");
    }

    var gambit = new Gambit(gambitData);
    var self = this;
    gambit.save(function (err) {
      if (err) {
        return callback(err);
      }
      self.gambits.addToSet(gambit._id);
      self.save(function (err2) {
        callback(err2, gambit);
      });
    });
  };

  topicSchema.methods.sortGambits = function (callback) {
    var self = this;
    var expandReorder = function (gambitId, cb) {
      Gambit.findById(gambitId, function (err, gambit) {
        if (err) {
          console.log(err);
        }
        cb(null, gambit);
      });
    };

    async.map(self.gambits, expandReorder, function (err, newGambitList) {
      if (err) {
        console.log(err);
      }

      var newList = Sort.sortTriggerSet(newGambitList);
      self.gambits = newList.map(function (g) {
        return g._id;
      });
      self.save(callback);
    });
  };

  topicSchema.methods.checkConditions = function(message, user, plugins, scope, localOptions, callback) {
    var self = this;
    // Okay, were is the user state?
    var conditionItor = function(cond, next) {

      var context = user.conversationState || {};

      debug("CheckItor - Context", context);
      debug("CheckItor - Condition", cond.condition);

      try {
        if (safeEval(cond.condition, context)) {
          debug("--- Condition TRUE YAY ---");

          var options = {
            message: message,
            user: user,
            plugins: plugins,
            scope: scope,
            topic: self.name,
            localOptions: localOptions
          };

          cond.doesMatch(options, next);
        } else {
          next(true);
        }
      } catch(e) {
        next(true);
      }
    };

    async.mapSeries(this.conditions, conditionItor, function(err, res) {
      if (err) {
        return callback(true, []);
      } else {
        callback(err, _.flatten(res));
      }
    });

  };

  topicSchema.methods.findMatch = function (message, user, plugins, scope, localOptions, callback) {
    var self = this;

    var options = {
      message: message,
      user: user,
      plugins: plugins,
      scope: scope,
      topic: this.name,
      localOptions: localOptions
    };

    Common.eachGambit("topic", self._id, options, callback);
  };

  // Lightweight match for one topic
  // TODO offload this to common
  topicSchema.methods.doesMatch = function (message, cb) {
    var self = this;

    var itor = function (gambit, next) {
      gambit.doesMatch(message, function (err, match2) {
        if (err) {
          console.log(err);
        }
        next(match2 ? gambit._id : null);
      });
    };

    Topic.findOne({name: self.name}, "gambits")
      .populate("gambits")
      .exec(function (err, mgambits) {
        if (err) {
          console.log(err);
        }
        async.filter(mgambits.gambits, itor, function (res) {
          cb(null, res);
        });
      }
    );
  };

  topicSchema.methods.clearGambits = function (callback) {
    var self = this;

    var clearGambit = function (gambitId, cb) {
      self.gambits.pull({ _id: gambitId });
      Gambit.findById(gambitId, function (err, gambit) {
        if (err) {
          console.log(err);
        }

        gambit.clearReplies(function() {
          Gambit.remove({ _id: gambitId }, function (err) {
            if (err) {
              console.log(err);
            }

            debug('removed gambit ' + gambitId);

            cb(null, gambitId);
          });
        });
      });
    };

    async.map(self.gambits, clearGambit, function (err, clearedGambits) {
      self.save(function (err2) {
        callback(err2, clearedGambits);
      });
    });
  };

  // This will find a gambit in any topic
  topicSchema.statics.findTriggerByTrigger = function (input, callback) {
    Gambit.findOne({input: input}).exec(callback);
  };

  topicSchema.statics.findByName = function (name, callback) {
    this.findOne({name: name}, {}, callback);
  };

  // Private function to score the topics by TF-IDF
  var _score = function (msg) {
    var docs = [];

    // Here we score the input aginst the topic kewords to come up with a topic order.
    tfidf.tfidfs(msg.lemString.tokenizeAndStem(), function (index, m, k) {

      // Filter out system topic pre/post
      if (k !== "__pre__" && k !== "__post__") {
        docs.push({topic: k, score: m});
      }
    });

    // Removes duplicate entries.
    docs = _.uniq(docs, function (item, key, a) {
      return item.topic;
    });

    var topicOrder = _.sortBy(docs, function (item) {
      return item.score;
    }).reverse();

    return _.map(topicOrder, function (item) {
      return {name: item.topic, score: item.score, type:'TOPIC'};
    });
  };

  exports.rootTopic = function (repId, cb) {
    _walkParent(repId, [], cb);
  };

  topicSchema.statics.findPendingTopicsForUser = function (user, msg, callback) {

    var self = this;
    var currentTopic = user.getTopic();
    var aTopics = [];
    var i;

    var scoredTopics = _score(msg);
    debug("Topic Score", scoredTopics);

    var removeMissingTopics = function(top) {
      return _.filter(top, function(item) {
        return item.id;
      });
    };

    self.find({}, function (err, allTopics) {
      if (err) {
        console.log(err);
      }

      // Add the current topic to the top of the stack.
      scoredTopics.unshift({name: currentTopic, type:'TOPIC'});

      var otherTopics = allTopics;
      otherTopics = _.map(otherTopics, function (item) {
        return {id: item._id, name: item.name, system: item.system};
      });

      // This gets a list if all the remaining topics.
      otherTopics = _.filter(otherTopics, function (obj) {
        return !_.findWhere(scoredTopics, {name: obj.name});
      });

      // We remove the system topics
      otherTopics = _.filter(otherTopics, function (obj) {
        return obj.system === false;
      });

      aTopics.push({name: "__pre__", type:"TOPIC"});

      for (i = 0; i < scoredTopics.length; i++) {
        if (scoredTopics[i].name !== "__post__" && scoredTopics[i].name !== "__pre__") {
          aTopics.push(scoredTopics[i]);
        }
      }

      for (i = 0; i < otherTopics.length; i++) {
        if (otherTopics[i].name !== "__post__" && otherTopics[i].name !== "__pre__") {
          otherTopics[i].type = "TOPIC";
          aTopics.push(otherTopics[i]);
        }
      }

      aTopics.push({name: "__post__", type:"TOPIC"});

      // Lets assign the ids to the topics
      for (var i = 0; i < aTopics.length; i++) {
        var tName = aTopics[i].name;
        for (var n = 0; n < allTopics.length; n++) {
          if (allTopics[n].name === tName) {
            aTopics[i].id = allTopics[n]._id;
          }
        }
      }

      // If we are currently in a conversation, we want the entire chain added
      // to the topics to search
      var lastReply = user.__history__.reply[0];
      if (!_.isEmpty(lastReply)) {

        // If the message is 5 Minutes old we continue
        var delta = new Date() - lastReply.createdAt;
        if (delta <= 1000 * 300) {
          var replyId = lastReply.replyId;
          var clearBit = lastReply.clearConvo;

          debug("Last reply: ", lastReply.raw, replyId, clearBit);

          if (clearBit === true) {
            debug("Conversation RESET by clearBit");
            callback(null, removeMissingTopics(aTopics));
          } else {
            Reply.findOne({_id: replyId}).exec(function (err, reply) {
              if (!reply) {
                debug("We couldn't match the last reply. Continuing.");
                callback(null, removeMissingTopics(aTopics));
              } else {
                Common.walkReplyParent(reply._id, function (err, replyThreads) {

                  replyThreads = replyThreads.map(function (item) {
                    return {id: item, type: "REPLY"};
                  });

                  replyThreads.unshift(1, 0);
                  Array.prototype.splice.apply(aTopics, replyThreads);

                  callback(null, removeMissingTopics(aTopics));
                });
              }
            });
          }
        } else {
          debug("The conversation thread was to old to continue it.");
          callback(null, removeMissingTopics(aTopics));
        }
      } else {
        callback(null, removeMissingTopics(aTopics));
      }
    });
  };

  topicSchema.plugin(findOrCreate);

  try {
    return mongoose.model("Topic", topicSchema);
  } catch(e) {
    return mongoose.model("Topic");
  }
};
<commit_msg>Add a little more debugging to TF-IDF score<commit_after>/*global Reply, Topic, Gambit */
/**

  Topics are a grouping of gambits.
  The order of the Gambits are important, and a gambit can live in more than one topic.

**/

var natural = require("natural");
var _ = require("lodash");
var async = require("async");
var findOrCreate = require("mongoose-findorcreate");
var debug = require("debug")("Topics");
var Sort = require("./sort");
var Common = require("./common");
var safeEval = require("safe-eval");

var TfIdf = natural.TfIdf;
var tfidf = new TfIdf();

module.exports = function (mongoose) {

  natural.PorterStemmer.attach();

  var topicSchema = new mongoose.Schema({
    name: {type: String, index: true, unique: true},
    keep: {type: Boolean, default: false },
    system: {type: Boolean, default: false},
    filter: {type: String, default: ""},
    keywords: {type: Array},
    gambits: [{ type: String, ref: "Gambit"}],
    conditions: [{ type: String, ref: "Condition"}]
  });

  topicSchema.pre("save", function (next) {
    var self = this;
    var kw;

    if (!_.isEmpty(this.keywords)) {
      kw = self.keywords.join(" ");
      if (kw) {
        tfidf.addDocument(kw.tokenizeAndStem(), self.name);
      }
    }
    next();
  });

  topicSchema.methods.createCondition = function(conditionData, callback) {
    var self = this;
    if (!conditionData) {
      return callback("No data");
    }

    if (_.isEmpty(conditionData.gambits)) {
      return callback("No gambits");
    } else {
      var gambits = [];
      var gambitLookup = function(gambit_id, next) {
        Gambit.findOne({id: gambit_id}, function(error, gambit) {
          gambits.push(gambit._id);
          next();
        });
      };

      async.each(conditionData.gambits, gambitLookup, function() {

        conditionData.gambits = gambits;
        var condition = new Condition(conditionData);

        condition.save(function (err) {
          if (err) {
            return callback(err);
          }
          self.conditions.addToSet(condition._id);
          self.save(function (err2) {
            callback(err2, condition);
          });
        });
      })
    }
  };

  // This will create the Gambit and add it to the model
  topicSchema.methods.createGambit = function (gambitData, callback) {
    if (!gambitData) {
      return callback("No data");
    }

    var gambit = new Gambit(gambitData);
    var self = this;
    gambit.save(function (err) {
      if (err) {
        return callback(err);
      }
      self.gambits.addToSet(gambit._id);
      self.save(function (err2) {
        callback(err2, gambit);
      });
    });
  };

  topicSchema.methods.sortGambits = function (callback) {
    var self = this;
    var expandReorder = function (gambitId, cb) {
      Gambit.findById(gambitId, function (err, gambit) {
        if (err) {
          console.log(err);
        }
        cb(null, gambit);
      });
    };

    async.map(self.gambits, expandReorder, function (err, newGambitList) {
      if (err) {
        console.log(err);
      }

      var newList = Sort.sortTriggerSet(newGambitList);
      self.gambits = newList.map(function (g) {
        return g._id;
      });
      self.save(callback);
    });
  };

  topicSchema.methods.checkConditions = function(message, user, plugins, scope, localOptions, callback) {
    var self = this;
    // Okay, were is the user state?
    var conditionItor = function(cond, next) {

      var context = user.conversationState || {};

      debug("CheckItor - Context", context);
      debug("CheckItor - Condition", cond.condition);

      try {
        if (safeEval(cond.condition, context)) {
          debug("--- Condition TRUE YAY ---");

          var options = {
            message: message,
            user: user,
            plugins: plugins,
            scope: scope,
            topic: self.name,
            localOptions: localOptions
          };

          cond.doesMatch(options, next);
        } else {
          next(true);
        }
      } catch(e) {
        next(true);
      }
    };

    async.mapSeries(this.conditions, conditionItor, function(err, res) {
      if (err) {
        return callback(true, []);
      } else {
        callback(err, _.flatten(res));
      }
    });

  };

  topicSchema.methods.findMatch = function (message, user, plugins, scope, localOptions, callback) {
    var self = this;

    var options = {
      message: message,
      user: user,
      plugins: plugins,
      scope: scope,
      topic: this.name,
      localOptions: localOptions
    };

    Common.eachGambit("topic", self._id, options, callback);
  };

  // Lightweight match for one topic
  // TODO offload this to common
  topicSchema.methods.doesMatch = function (message, cb) {
    var self = this;

    var itor = function (gambit, next) {
      gambit.doesMatch(message, function (err, match2) {
        if (err) {
          console.log(err);
        }
        next(match2 ? gambit._id : null);
      });
    };

    Topic.findOne({name: self.name}, "gambits")
      .populate("gambits")
      .exec(function (err, mgambits) {
        if (err) {
          console.log(err);
        }
        async.filter(mgambits.gambits, itor, function (res) {
          cb(null, res);
        });
      }
    );
  };

  topicSchema.methods.clearGambits = function (callback) {
    var self = this;

    var clearGambit = function (gambitId, cb) {
      self.gambits.pull({ _id: gambitId });
      Gambit.findById(gambitId, function (err, gambit) {
        if (err) {
          console.log(err);
        }

        gambit.clearReplies(function() {
          Gambit.remove({ _id: gambitId }, function (err) {
            if (err) {
              console.log(err);
            }

            debug('removed gambit ' + gambitId);

            cb(null, gambitId);
          });
        });
      });
    };

    async.map(self.gambits, clearGambit, function (err, clearedGambits) {
      self.save(function (err2) {
        callback(err2, clearedGambits);
      });
    });
  };

  // This will find a gambit in any topic
  topicSchema.statics.findTriggerByTrigger = function (input, callback) {
    Gambit.findOne({input: input}).exec(callback);
  };

  topicSchema.statics.findByName = function (name, callback) {
    this.findOne({name: name}, {}, callback);
  };

  // Private function to score the topics by TF-IDF
  var _score = function (msg) {
    var docs = [];
    var tas = msg.lemString.tokenizeAndStem();
    debug("Token And Stem Words", tas);
    // Here we score the input aginst the topic kewords to come up with a topic order.
    tfidf.tfidfs(tas, function (index, m, k) {

      // Filter out system topic pre/post
      if (k !== "__pre__" && k !== "__post__") {
        docs.push({topic: k, score: m});
      }
    });

    // Removes duplicate entries.
    docs = _.uniq(docs, function (item, key, a) {
      return item.topic;
    });

    debug("Score Inner", docs);
    var topicOrder = _.sortBy(docs, function (item) {
      return item.score;
    }).reverse();

    return _.map(topicOrder, function (item) {
      return {name: item.topic, score: item.score, type:'TOPIC'};
    });
  };

  exports.rootTopic = function (repId, cb) {
    _walkParent(repId, [], cb);
  };

  topicSchema.statics.findPendingTopicsForUser = function (user, msg, callback) {

    var self = this;
    var currentTopic = user.getTopic();
    var aTopics = [];
    var i;

    var scoredTopics = _score(msg);
    debug("Topic Score", scoredTopics);

    var removeMissingTopics = function(top) {
      return _.filter(top, function(item) {
        return item.id;
      });
    };

    self.find({}, function (err, allTopics) {
      if (err) {
        console.log(err);
      }

      // Add the current topic to the top of the stack.
      scoredTopics.unshift({name: currentTopic, type:'TOPIC'});

      var otherTopics = allTopics;
      otherTopics = _.map(otherTopics, function (item) {
        return {id: item._id, name: item.name, system: item.system};
      });

      // This gets a list if all the remaining topics.
      otherTopics = _.filter(otherTopics, function (obj) {
        return !_.findWhere(scoredTopics, {name: obj.name});
      });

      // We remove the system topics
      otherTopics = _.filter(otherTopics, function (obj) {
        return obj.system === false;
      });

      aTopics.push({name: "__pre__", type:"TOPIC"});

      for (i = 0; i < scoredTopics.length; i++) {
        if (scoredTopics[i].name !== "__post__" && scoredTopics[i].name !== "__pre__") {
          aTopics.push(scoredTopics[i]);
        }
      }

      for (i = 0; i < otherTopics.length; i++) {
        if (otherTopics[i].name !== "__post__" && otherTopics[i].name !== "__pre__") {
          otherTopics[i].type = "TOPIC";
          aTopics.push(otherTopics[i]);
        }
      }

      aTopics.push({name: "__post__", type:"TOPIC"});

      // Lets assign the ids to the topics
      for (var i = 0; i < aTopics.length; i++) {
        var tName = aTopics[i].name;
        for (var n = 0; n < allTopics.length; n++) {
          if (allTopics[n].name === tName) {
            aTopics[i].id = allTopics[n]._id;
          }
        }
      }

      // If we are currently in a conversation, we want the entire chain added
      // to the topics to search
      var lastReply = user.__history__.reply[0];
      if (!_.isEmpty(lastReply)) {

        // If the message is 5 Minutes old we continue
        var delta = new Date() - lastReply.createdAt;
        if (delta <= 1000 * 300) {
          var replyId = lastReply.replyId;
          var clearBit = lastReply.clearConvo;

          debug("Last reply: ", lastReply.raw, replyId, clearBit);

          if (clearBit === true) {
            debug("Conversation RESET by clearBit");
            callback(null, removeMissingTopics(aTopics));
          } else {
            Reply.findOne({_id: replyId}).exec(function (err, reply) {
              if (!reply) {
                debug("We couldn't match the last reply. Continuing.");
                callback(null, removeMissingTopics(aTopics));
              } else {
                Common.walkReplyParent(reply._id, function (err, replyThreads) {

                  replyThreads = replyThreads.map(function (item) {
                    return {id: item, type: "REPLY"};
                  });

                  replyThreads.unshift(1, 0);
                  Array.prototype.splice.apply(aTopics, replyThreads);

                  callback(null, removeMissingTopics(aTopics));
                });
              }
            });
          }
        } else {
          debug("The conversation thread was to old to continue it.");
          callback(null, removeMissingTopics(aTopics));
        }
      } else {
        callback(null, removeMissingTopics(aTopics));
      }
    });
  };

  topicSchema.plugin(findOrCreate);

  try {
    return mongoose.model("Topic", topicSchema);
  } catch(e) {
    return mongoose.model("Topic");
  }
};
<|endoftext|>


<commit_before>'use strict';
const MongoError = require('./error').MongoError;

let TxnState;
let stateMachine;

(() => {
  const NO_TRANSACTION = 'NO_TRANSACTION';
  const STARTING_TRANSACTION = 'STARTING_TRANSACTION';
  const TRANSACTION_IN_PROGRESS = 'TRANSACTION_IN_PROGRESS';
  const TRANSACTION_COMMITTED = 'TRANSACTION_COMMITTED';
  const TRANSACTION_COMMITTED_EMPTY = 'TRANSACTION_COMMITTED_EMPTY';
  const TRANSACTION_ABORTED = 'TRANSACTION_ABORTED';

  TxnState = {
    NO_TRANSACTION,
    STARTING_TRANSACTION,
    TRANSACTION_IN_PROGRESS,
    TRANSACTION_COMMITTED,
    TRANSACTION_COMMITTED_EMPTY,
    TRANSACTION_ABORTED
  };

  stateMachine = {
    [NO_TRANSACTION]: [NO_TRANSACTION, STARTING_TRANSACTION],
    [STARTING_TRANSACTION]: [
      TRANSACTION_IN_PROGRESS,
      TRANSACTION_COMMITTED,
      TRANSACTION_COMMITTED_EMPTY,
      TRANSACTION_ABORTED
    ],
    [TRANSACTION_IN_PROGRESS]: [
      TRANSACTION_IN_PROGRESS,
      TRANSACTION_COMMITTED,
      TRANSACTION_ABORTED
    ],
    [TRANSACTION_COMMITTED]: [
      TRANSACTION_COMMITTED,
      TRANSACTION_COMMITTED_EMPTY,
      STARTING_TRANSACTION,
      NO_TRANSACTION
    ],
    [TRANSACTION_ABORTED]: [STARTING_TRANSACTION, NO_TRANSACTION],
    [TRANSACTION_COMMITTED_EMPTY]: [TRANSACTION_COMMITTED_EMPTY, NO_TRANSACTION]
  };
})();

/**
 * The MongoDB ReadConcern, which allows for control of the consistency and isolation properties
 * of the data read from replica sets and replica set shards.
 * @typedef {Object} ReadConcern
 * @property {'local'|'available'|'majority'|'linearizable'|'snapshot'} level The readConcern Level
 * @see https://docs.mongodb.com/manual/reference/read-concern/
 */

/**
 * A MongoDB WriteConcern, which describes the level of acknowledgement
 * requested from MongoDB for write operations.
 * @typedef {Object} WriteConcern
 * @property {number|'majority'|string} [w=1] requests acknowledgement that the write operation has
 * propagated to a specified number of mongod hosts
 * @property {boolean} [j=false] requests acknowledgement from MongoDB that the write operation has
 * been written to the journal
 * @property {number} [wtimeout] a time limit, in milliseconds, for the write concern
 * @see https://docs.mongodb.com/manual/reference/write-concern/
 */

/**
 * Configuration options for a transaction.
 * @typedef {Object} TransactionOptions
 * @property {ReadConcern} [readConcern] A default read concern for commands in this transaction
 * @property {WriteConcern} [writeConcern] A default writeConcern for commands in this transaction
 * @property {ReadPreference} [readPreference] A default read preference for commands in this transaction
 */

/**
 * A class maintaining state related to a server transaction. Internal Only
 * @ignore
 */
class Transaction {
  /**
   * Create a transaction
   *
   * @ignore
   * @param {TransactionOptions} [options] Optional settings
   */
  constructor(options) {
    options = options || {};

    this.state = TxnState.NO_TRANSACTION;
    this.options = {};

    if (options.writeConcern || typeof options.w !== 'undefined') {
      const w = options.writeConcern ? options.writeConcern.w : options.w;
      if (w <= 0) {
        throw new MongoError('Transactions do not support unacknowledged write concern');
      }

      this.options.writeConcern = options.writeConcern ? options.writeConcern : { w: options.w };
    }

    if (options.readConcern) this.options.readConcern = options.readConcern;
    if (options.readPreference) this.options.readPreference = options.readPreference;
  }

  /**
   * @ignore
   * @return Whether this session is presently in a transaction
   */
  get isActive() {
    return (
      [TxnState.STARTING_TRANSACTION, TxnState.TRANSACTION_IN_PROGRESS].indexOf(this.state) !== -1
    );
  }

  /**
   * Transition the transaction in the state machine
   * @ignore
   * @param {TxnState} state The new state to transition to
   */
  transition(nextState) {
    const nextStates = stateMachine[this.state];
    if (nextStates && nextStates.indexOf(nextState) !== -1) {
      this.state = nextState;
      return;
    }

    throw new MongoError(
      `Attempted illegal state transition from [${this.state}] to [${nextState}]`
    );
  }
}

module.exports = { TxnState, Transaction };
<commit_msg>feat: add ability to pin server to transaction state machine<commit_after>'use strict';
const MongoError = require('./error').MongoError;

let TxnState;
let stateMachine;

(() => {
  const NO_TRANSACTION = 'NO_TRANSACTION';
  const STARTING_TRANSACTION = 'STARTING_TRANSACTION';
  const TRANSACTION_IN_PROGRESS = 'TRANSACTION_IN_PROGRESS';
  const TRANSACTION_COMMITTED = 'TRANSACTION_COMMITTED';
  const TRANSACTION_COMMITTED_EMPTY = 'TRANSACTION_COMMITTED_EMPTY';
  const TRANSACTION_ABORTED = 'TRANSACTION_ABORTED';

  TxnState = {
    NO_TRANSACTION,
    STARTING_TRANSACTION,
    TRANSACTION_IN_PROGRESS,
    TRANSACTION_COMMITTED,
    TRANSACTION_COMMITTED_EMPTY,
    TRANSACTION_ABORTED
  };

  stateMachine = {
    [NO_TRANSACTION]: [NO_TRANSACTION, STARTING_TRANSACTION],
    [STARTING_TRANSACTION]: [
      TRANSACTION_IN_PROGRESS,
      TRANSACTION_COMMITTED,
      TRANSACTION_COMMITTED_EMPTY,
      TRANSACTION_ABORTED
    ],
    [TRANSACTION_IN_PROGRESS]: [
      TRANSACTION_IN_PROGRESS,
      TRANSACTION_COMMITTED,
      TRANSACTION_ABORTED
    ],
    [TRANSACTION_COMMITTED]: [
      TRANSACTION_COMMITTED,
      TRANSACTION_COMMITTED_EMPTY,
      STARTING_TRANSACTION,
      NO_TRANSACTION
    ],
    [TRANSACTION_ABORTED]: [STARTING_TRANSACTION, NO_TRANSACTION],
    [TRANSACTION_COMMITTED_EMPTY]: [TRANSACTION_COMMITTED_EMPTY, NO_TRANSACTION]
  };
})();

/**
 * The MongoDB ReadConcern, which allows for control of the consistency and isolation properties
 * of the data read from replica sets and replica set shards.
 * @typedef {Object} ReadConcern
 * @property {'local'|'available'|'majority'|'linearizable'|'snapshot'} level The readConcern Level
 * @see https://docs.mongodb.com/manual/reference/read-concern/
 */

/**
 * A MongoDB WriteConcern, which describes the level of acknowledgement
 * requested from MongoDB for write operations.
 * @typedef {Object} WriteConcern
 * @property {number|'majority'|string} [w=1] requests acknowledgement that the write operation has
 * propagated to a specified number of mongod hosts
 * @property {boolean} [j=false] requests acknowledgement from MongoDB that the write operation has
 * been written to the journal
 * @property {number} [wtimeout] a time limit, in milliseconds, for the write concern
 * @see https://docs.mongodb.com/manual/reference/write-concern/
 */

/**
 * Configuration options for a transaction.
 * @typedef {Object} TransactionOptions
 * @property {ReadConcern} [readConcern] A default read concern for commands in this transaction
 * @property {WriteConcern} [writeConcern] A default writeConcern for commands in this transaction
 * @property {ReadPreference} [readPreference] A default read preference for commands in this transaction
 */

/**
 * A class maintaining state related to a server transaction. Internal Only
 * @ignore
 */
class Transaction {
  /**
   * Create a transaction
   *
   * @ignore
   * @param {TransactionOptions} [options] Optional settings
   */
  constructor(options) {
    options = options || {};

    this.state = TxnState.NO_TRANSACTION;
    this.options = {};

    if (options.writeConcern || typeof options.w !== 'undefined') {
      const w = options.writeConcern ? options.writeConcern.w : options.w;
      if (w <= 0) {
        throw new MongoError('Transactions do not support unacknowledged write concern');
      }

      this.options.writeConcern = options.writeConcern ? options.writeConcern : { w: options.w };
    }

    if (options.readConcern) this.options.readConcern = options.readConcern;
    if (options.readPreference) this.options.readPreference = options.readPreference;

    // TODO: This isn't technically necessary
    this._pinnedServer = undefined;
  }

  get server() {
    return this._pinnedServer;
  }

  /**
   * @ignore
   * @return Whether this session is presently in a transaction
   */
  get isActive() {
    return (
      [TxnState.STARTING_TRANSACTION, TxnState.TRANSACTION_IN_PROGRESS].indexOf(this.state) !== -1
    );
  }

  /**
   * Transition the transaction in the state machine
   * @ignore
   * @param {TxnState} state The new state to transition to
   */
  transition(nextState) {
    const nextStates = stateMachine[this.state];
    if (nextStates && nextStates.indexOf(nextState) !== -1) {
      this.state = nextState;
      if (this.state === TxnState.NO_TRANSACTION) {
        this.unpinServer();
      }
      return;
    }

    throw new MongoError(
      `Attempted illegal state transition from [${this.state}] to [${nextState}]`
    );
  }

  pinServer(server) {
    if (this.isActive) {
      this._pinnedServer = server;
    }
  }

  unpinServer() {
    this._pinnedServer = undefined;
  }
}

module.exports = { TxnState, Transaction };
<|endoftext|>


<commit_before>_ = require('underscore');
var Promise = require('bluebird');

function Transactions() {
  /** @type {Object} */
  this.list = {};
}

/**
 * @param {String} id
 * @param {Function} executeCallback
 */
Transactions.prototype.add = function(id, executeCallback) {
  if (this._has(id)) {
    throw new Error('Transaction with id: `' + id + '` already stored');
  }
  if (!(executeCallback instanceof Function)) {
    throw new Error('`executeCallback` must be `Function`');
  }
  this.list[id] = executeCallback;

};

/**
 * @param {String} id
 * @returns {Function|Null}
 */
Transactions.prototype.find = function(id) {
  if (!this._has(id)) {
    return null;
  }
  return this.list[id];
};

/**
 * @param {String} id
 * @param {Object} message
 * @returns {Promise}
 */
Transactions.prototype.execute = function(id, message) {
  switch (message['janus']) {
    case 'ack':
      return Promise.resolve();
    case 'error':
      this.remove(id);
      return Promise.resolve();
    default:
      return this.find(id).call(null, message).finally(function() {
        console.log('removing', id);
        this.remove(id);
      }.bind(this));
  }
};

/**
 * @param {String} id
 */
Transactions.prototype.remove = function(id) {
  if (!this._has(id)) {
    throw new Error('Transaction with id: `' + id + "` doesn't exist");
  }
  delete this.list[id];
};

/**
 * @param {String} id
 * @returns {Boolean}
 */
Transactions.prototype._has = function(id) {
  return _.has(this.list, id);
};

module.exports = Transactions;
<commit_msg>Throw if transaction not present in execute<commit_after>_ = require('underscore');
var Promise = require('bluebird');

function Transactions() {
  /** @type {Object} */
  this.list = {};
}

/**
 * @param {String} id
 * @param {Function} executeCallback
 */
Transactions.prototype.add = function(id, executeCallback) {
  if (this._has(id)) {
    throw new Error('Transaction with id: `' + id + '` already stored');
  }
  if (!(executeCallback instanceof Function)) {
    throw new Error('`executeCallback` must be `Function`');
  }
  this.list[id] = executeCallback;

};

/**
 * @param {String} id
 * @returns {Function|Null}
 */
Transactions.prototype.find = function(id) {
  if (!this._has(id)) {
    return null;
  }
  return this.list[id];
};

/**
 * @param {String} id
 * @param {Object} message
 * @returns {Promise}
 */
Transactions.prototype.execute = function(id, message) {
  var transaction = this.find(id);
  if (!transaction) {
    throw new Error('Transaction `' + id + '` not found');
  }

  switch (message['janus']) {
    case 'ack':
      return Promise.resolve();
    case 'error':
      this.remove(id);
      return Promise.resolve();
    default:
      return transaction.call(null, message).finally(function() {
        this.remove(id);
      }.bind(this));
  }
};

/**
 * @param {String} id
 */
Transactions.prototype.remove = function(id) {
  if (!this._has(id)) {
    throw new Error('Transaction with id: `' + id + "` doesn't exist");
  }
  delete this.list[id];
};

/**
 * @param {String} id
 * @returns {Boolean}
 */
Transactions.prototype._has = function(id) {
  return _.has(this.list, id);
};

module.exports = Transactions;
<|endoftext|>


<commit_before>var Lang = A.Lang,

	getClassName = A.ClassNameManager.getClassName,

	NAME = 'field',

	getTypeClassName = A.cached(
		function(type, prefix) {
			var base = ['field'];

			if (prefix) {
				base.push(prefix);
			}

			base = base.join('-');

			var className = [getClassName(base, type)];

			if (type == 'password') {
				className.push(getClassName(base, 'text'));
			}

			return className.join(' ');
		}
	),

	CSS_FIELD = getClassName(NAME),
	CSS_FIELD_CONTENT = getClassName(NAME, 'content'),
	CSS_FIELD_INPUT = getClassName(NAME, 'input'),
	CSS_FIELD_HINT = getClassName(NAME, 'hint'),
	CSS_FIELD_INVALID = getClassName(NAME, 'invalid'),
	CSS_FIELD_LABEL = getClassName(NAME, 'label'),

	CSS_LABELS = getClassName(NAME, 'labels'),
	CSS_LABELS_INLINE = getClassName(NAME, 'labels', 'inline'),

	CSS_LABEL_ALIGN = {
		left: [CSS_LABELS, 'left'].join('-'),
		right: [CSS_LABELS, 'right'].join('-'),
		top: [CSS_LABELS, 'top'].join('-')
	},

	REGEX_INLINE_LABEL = /left|right/,

	TPL_BOUNDING_BOX = '',
	TPL_CONTENT_BOX = '',
	TPL_FIELD_HINT = '',
	TPL_INPUT = '<input autocomplete="off" class="{cssClass}" id="{id}" name="{name}" type="{type}" />',
	TPL_LABEL = '',

	_FIELD_INSTANCES = {};

var Field = A.Component.create(
	{
		NAME: NAME,

		ATTRS: {
			readOnly: {
				value: false
			},

			name: {
				value: '',
				getter: function(value) {
					var instance = this;

					return value || instance.get('id');
				}
			},

			id: {
				getter: function(value) {
					var instance = this;

					var node = this.get('node');

					if (node) {
						value = node.get('id');
					}

					if (!value) {
						value = A.guid();
					}

					return value;
				}
			},

			type: {
				value: 'text',
				writeOnce: true
			},

			labelAlign: {
				value: ''
			},

			labelNode: {
				valueFn: function() {
					var instance = this;

					return A.Node.create(TPL_LABEL);
				}
			},

			labelText: {
				valueFn: function() {
					var instance = this;

					return instance.get('labelNode').get('innerHTML');
				},

				setter: function(value) {
					var instance = this;

					instance.get('labelNode').set('innerHTML', value);

					return value;
				}
			},

			node: {
				value: null,
				setter: function(value) {
					var instance = this;

					return A.one(value) || instance._createFieldNode();
				}
			},

			fieldHint: {
				value: ''
			},

			fieldHintNode: {
				value: null,
				setter: function(value) {
					var instance = this;

					return A.one(value) || instance._createFieldHint();
				}
			},

			prevVal: {
				value: ''
			},

			valid: {
				value: true,
				getter: function(value) {
					var instance = this;

					var validator = instance.get('validator');

					var valid = instance.get('disabled') || validator(instance.get('value'));

					return valid;
				}
			},

			dirty: {
				value: false,
				getter: function(value) {
					var instance = this;

					if (instance.get('disabled')) {
						value = false;
					}
					else {
						var currentVal = String(instance.get('value'));
						var prevVal = String(instance.get('prevVal'));

						value = (currentVal !== prevVal);
					}

					return value;
				}
			},

			size: {},

			validator: {
				valueFn: function() {
					var instance = this;

					return instance.fieldValidator;
				},
				validator: Lang.isFunction
			},

			value: {
				getter: '_getNodeValue',
				setter: '_setNodeValue',
				validator: 'fieldValidator'
			}
		},

		HTML_PARSER: {
			labelNode: 'label',
			node: 'input, textarea, select'
		},

		BIND_UI_ATTRS: [
			'id',
			'readOnly',
			'name',
			'size',
			'tabIndex',
			'type',
			'value'
		],

		getTypeClassName: getTypeClassName,

		getField: function(field) {
			var fieldWidget = null;

			if (field instanceof A.Field) {
				fieldWidget = field;
			}
			else if (field && (Lang.isString(field) || field instanceof A.Node || field.nodeName)) {
				var fieldId = A.one(field).get('id');

				fieldWidget = _FIELD_INSTANCES[fieldId];

				if (!fieldWidget) {
					var boundingBox = field.ancestor('.aui-field');
					var contentBox = field.ancestor('.aui-field-content');

					fieldWidget = new Field(
						{
							boundingBox: boundingBox,
							contentBox: contentBox,
							node: field
						}
					);
				}
			}
			else if (Lang.isObject(field)) {
				fieldWidget = new Field(field);
			}

			return fieldWidget;
		},

		prototype: {
			BOUNDING_TEMPLATE: TPL_BOUNDING_BOX,
			CONTENT_TEMPLATE: TPL_CONTENT_BOX,
			FIELD_TEMPLATE: TPL_INPUT,
			FIELD_TYPE: 'text',

			initializer: function() {
				var instance = this;

				var id = instance.get('node').guid();

				_FIELD_INSTANCES[id] = instance;
			},

			renderUI: function() {
				var instance = this;

				instance._renderField();
				instance._renderLabel();
				instance._renderFieldHint();
			},

			bindUI: function() {
				var instance = this;

				instance.after('labelAlignChange', instance._afterLabelAlignChange);
				instance.after('fieldHintChange', instance._afterFieldHintChange);
			},

			syncUI: function() {
				var instance = this;

				instance.set('prevVal', instance.get('value'));
			},

			fieldValidator: function(value) {
				var instance = this;

				return true;
			},

			isValid: function() {
				var instance = this;

				return instance.get('valid');
			},

			isDirty: function() {
				var instance = this;

				return instance.get('dirty');
			},

			resetValue: function() {
				var instance = this;

				instance.set('value', instance.get('prevVal'));

				instance.clearInvalid();
			},

			markInvalid: function(message) {
				var instance = this;

				instance.set('fieldHint', message);
				instance.get('fieldHintNode').show();

				instance.get('boundingBox').addClass(CSS_FIELD_INVALID);
			},

			clearInvalid: function() {
				var instance = this;

				instance.reset('fieldHint');

				if (!instance.get('fieldHint')) {
					instance.get('fieldHintNode').hide();
				}

				instance.get('boundingBox').removeClass(CSS_FIELD_INVALID);
			},

			validate: function() {
				var instance = this;

				var valid = instance.get('valid');

				if (valid) {
					instance.clearInvalid();
				}

				return valid;
			},

			_afterFieldHintChange: function(event) {
				var instance = this;

				instance._uiSetFieldHint(event.newVal, event.prevVal);
			},

			_afterLabelAlignChange: function(event) {
				var instance = this;

				instance._uiSetLabelAlign(event.newVal, event.prevVal);
			},

			_createFieldHint: function() {
				var instance = this;

				var fieldHint = A.Node.create(TPL_FIELD_HINT);

				instance.get('contentBox').append(fieldHint);

				return fieldHint;
			},

			_createFieldNode: function() {
				var instance = this;

				var fieldTemplate = instance.FIELD_TEMPLATE;

				instance.FIELD_TEMPLATE = A.substitute(
					fieldTemplate,
					{
						cssClass: CSS_FIELD_INPUT,
						id: instance.get('id'),
						name: instance.get('name'),
						type: instance.get('type')
					}
				);

				return A.Node.create(instance.FIELD_TEMPLATE);
			},

			_getNodeValue: function() {
				var instance = this;

				return instance.get('node').val();
			},

			_renderField: function() {
				var instance = this;

				var node = instance.get('node');

				node.val(instance.get('value'));

				var boundingBox = instance.get('boundingBox');
				var contentBox = instance.get('contentBox');

				var type = instance.get('type');

				boundingBox.addClass(getTypeClassName(type));
				node.addClass(getTypeClassName(type, 'input'));

				if (!contentBox.contains(node)) {
					if (node.inDoc()) {
						node.placeBefore(boundingBox);

						contentBox.appendChild(node);
					}
					else {
						contentBox.appendChild(node);
					}
				}

				boundingBox.removeAttribute('tabIndex');
			},

			_renderFieldHint: function() {
				var instance = this;

				var fieldHint = instance.get('fieldHint');

				if (fieldHint) {
					instance._uiSetFieldHint(fieldHint);
				}
			},

			_renderLabel: function() {
				var instance = this;

				var labelText = instance.get('labelText');

				if (labelText !== false) {
					var node = instance.get('node');
					var id = node.guid();

					labelText = instance.get('labelText');

					var labelNode = instance.get('labelNode');

					labelNode.addClass(getClassName(instance.name, 'label'));
					labelNode.setAttribute('for', id);
					labelNode.set('innerHTML', labelText);

					instance._uiSetLabelAlign(instance.get('labelAlign'));

					var contentBox = instance.get('contentBox');

					contentBox.prepend(labelNode);
				}
			},

			_setNodeValue: function(value) {
				var instance = this;

				instance._uiSetValue(value);

				return value;
			},

			_uiSetFieldHint: function(newVal, prevVal) {
				var instance = this;

				instance.get('fieldHintNode').set('innerHTML', newVal);
			},

			_uiSetId: function(newVal, src) {
				var instance = this;

				instance.get('node').set('id', newVal);
			},

			_uiSetLabelAlign: function(newVal, prevVal) {
				var instance = this;

				var boundingBox = instance.get('boundingBox');

				boundingBox.replaceClass(CSS_LABEL_ALIGN[prevVal], CSS_LABEL_ALIGN[newVal]);

				var action = 'removeClass';

				if (REGEX_INLINE_LABEL.test(newVal)) {
					action = 'addClass';
				}

				boundingBox[action](CSS_LABELS_INLINE);
			},

			_uiSetName: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('name', newVal);
			},

			_uiSetReadOnly: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('readOnly', newVal);
			},

			_uiSetSize: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('size', newVal);
			},

			_uiSetTabIndex: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('tabIndex', newVal);
			},

			_uiSetValue: function(newVal, src) {
				var instance = this;

				instance.get('node').val(newVal);
			},

			_requireAddAttr: false
		}
	}
);

A.Field = Field;<commit_msg>AUI-323 A.Field puts label before input when type is checkbox or radio<commit_after>var Lang = A.Lang,

	getClassName = A.ClassNameManager.getClassName,

	CLEARFIX = 'clearfix',
	HELPER = 'helper',
	NAME = 'field',
	SPACE = ' ',

	getTypeClassName = A.cached(
		function(type, prefix) {
			var base = ['field'];

			if (prefix) {
				base.push(prefix);
			}

			base = base.join('-');

			var className = [getClassName(base, type)];

			if (type == 'password') {
				className.push(getClassName(base, 'text'));
			}

			return className.join(' ');
		}
	),

	CSS_FIELD = getClassName(NAME),
	CSS_FIELD_CONTENT = getClassName(NAME, 'content'),
	CSS_FIELD_INPUT = getClassName(NAME, 'input'),
	CSS_FIELD_HINT = getClassName(NAME, 'hint'),
	CSS_FIELD_INVALID = getClassName(NAME, 'invalid'),
	CSS_FIELD_LABEL = getClassName(NAME, 'label'),

	CSS_HELPER_CLEARFIX = getClassName(HELPER, CLEARFIX),

	CSS_LABELS = getClassName(NAME, 'labels'),
	CSS_LABELS_INLINE = getClassName(NAME, 'labels', 'inline'),

	CSS_LABEL_ALIGN = {
		left: [CSS_LABELS, 'left'].join('-'),
		right: [CSS_LABELS, 'right'].join('-'),
		top: [CSS_LABELS, 'top'].join('-')
	},

	REGEX_INLINE_LABEL = /left|right/,

	TPL_BOUNDING_BOX = '',
	TPL_CONTENT_BOX = '',
	TPL_FIELD_HINT = '',
	TPL_INPUT = '<input autocomplete="off" class="{cssClass}" id="{id}" name="{name}" type="{type}" />',
	TPL_LABEL = '',

	_FIELD_INSTANCES = {};

var Field = A.Component.create(
	{
		NAME: NAME,

		ATTRS: {
			readOnly: {
				value: false
			},

			name: {
				value: '',
				getter: function(value) {
					var instance = this;

					return value || instance.get('id');
				}
			},

			id: {
				getter: function(value) {
					var instance = this;

					var node = this.get('node');

					if (node) {
						value = node.get('id');
					}

					if (!value) {
						value = A.guid();
					}

					return value;
				}
			},

			type: {
				value: 'text',
				validator: Lang.isString,
				writeOnce: true
			},

			labelAlign: {
				value: ''
			},

			labelNode: {
				valueFn: function() {
					var instance = this;

					return A.Node.create(TPL_LABEL);
				}
			},

			labelText: {
				valueFn: function() {
					var instance = this;

					return instance.get('labelNode').get('innerHTML');
				},

				setter: function(value) {
					var instance = this;

					instance.get('labelNode').set('innerHTML', value);

					return value;
				}
			},

			node: {
				value: null,
				setter: function(value) {
					var instance = this;

					return A.one(value) || instance._createFieldNode();
				}
			},

			fieldHint: {
				value: ''
			},

			fieldHintNode: {
				value: null,
				setter: function(value) {
					var instance = this;

					return A.one(value) || instance._createFieldHint();
				}
			},

			prevVal: {
				value: ''
			},

			valid: {
				value: true,
				getter: function(value) {
					var instance = this;

					var validator = instance.get('validator');

					var valid = instance.get('disabled') || validator(instance.get('value'));

					return valid;
				}
			},

			dirty: {
				value: false,
				getter: function(value) {
					var instance = this;

					if (instance.get('disabled')) {
						value = false;
					}
					else {
						var currentVal = String(instance.get('value'));
						var prevVal = String(instance.get('prevVal'));

						value = (currentVal !== prevVal);
					}

					return value;
				}
			},

			size: {},

			validator: {
				valueFn: function() {
					var instance = this;

					return instance.fieldValidator;
				},
				validator: Lang.isFunction
			},

			value: {
				getter: '_getNodeValue',
				setter: '_setNodeValue',
				validator: 'fieldValidator'
			}
		},

		HTML_PARSER: {
			labelNode: 'label',
			node: 'input, textarea, select'
		},

		BIND_UI_ATTRS: [
			'id',
			'readOnly',
			'name',
			'size',
			'tabIndex',
			'type',
			'value'
		],

		getTypeClassName: getTypeClassName,

		getField: function(field) {
			var fieldWidget = null;

			if (field instanceof A.Field) {
				fieldWidget = field;
			}
			else if (field && (Lang.isString(field) || field instanceof A.Node || field.nodeName)) {
				var fieldId = A.one(field).get('id');

				fieldWidget = _FIELD_INSTANCES[fieldId];

				if (!fieldWidget) {
					var boundingBox = field.ancestor('.aui-field');
					var contentBox = field.ancestor('.aui-field-content');

					fieldWidget = new Field(
						{
							boundingBox: boundingBox,
							contentBox: contentBox,
							node: field
						}
					);
				}
			}
			else if (Lang.isObject(field)) {
				fieldWidget = new Field(field);
			}

			return fieldWidget;
		},

		prototype: {
			BOUNDING_TEMPLATE: TPL_BOUNDING_BOX,
			CONTENT_TEMPLATE: TPL_CONTENT_BOX,
			FIELD_TEMPLATE: TPL_INPUT,
			FIELD_TYPE: 'text',

			initializer: function() {
				var instance = this;

				var id = instance.get('node').guid();

				_FIELD_INSTANCES[id] = instance;
			},

			renderUI: function() {
				var instance = this;

				instance._renderField();
				instance._renderLabel();
				instance._renderFieldHint();
			},

			bindUI: function() {
				var instance = this;

				instance.after('labelAlignChange', instance._afterLabelAlignChange);
				instance.after('fieldHintChange', instance._afterFieldHintChange);
			},

			syncUI: function() {
				var instance = this;

				instance.set('prevVal', instance.get('value'));
			},

			fieldValidator: function(value) {
				var instance = this;

				return true;
			},

			isValid: function() {
				var instance = this;

				return instance.get('valid');
			},

			isDirty: function() {
				var instance = this;

				return instance.get('dirty');
			},

			resetValue: function() {
				var instance = this;

				instance.set('value', instance.get('prevVal'));

				instance.clearInvalid();
			},

			markInvalid: function(message) {
				var instance = this;

				instance.set('fieldHint', message);
				instance.get('fieldHintNode').show();

				instance.get('boundingBox').addClass(CSS_FIELD_INVALID);
			},

			clearInvalid: function() {
				var instance = this;

				instance.reset('fieldHint');

				if (!instance.get('fieldHint')) {
					instance.get('fieldHintNode').hide();
				}

				instance.get('boundingBox').removeClass(CSS_FIELD_INVALID);
			},

			validate: function() {
				var instance = this;

				var valid = instance.get('valid');

				if (valid) {
					instance.clearInvalid();
				}

				return valid;
			},

			_afterFieldHintChange: function(event) {
				var instance = this;

				instance._uiSetFieldHint(event.newVal, event.prevVal);
			},

			_afterLabelAlignChange: function(event) {
				var instance = this;

				instance._uiSetLabelAlign(event.newVal, event.prevVal);
			},

			_createFieldHint: function() {
				var instance = this;

				var fieldHint = A.Node.create(TPL_FIELD_HINT);

				instance.get('contentBox').append(fieldHint);

				return fieldHint;
			},

			_createFieldNode: function() {
				var instance = this;

				var fieldTemplate = instance.FIELD_TEMPLATE;

				instance.FIELD_TEMPLATE = A.substitute(
					fieldTemplate,
					{
						cssClass: CSS_FIELD_INPUT,
						id: instance.get('id'),
						name: instance.get('name'),
						type: instance.get('type')
					}
				);

				return A.Node.create(instance.FIELD_TEMPLATE);
			},

			_getNodeValue: function() {
				var instance = this;

				return instance.get('node').val();
			},

			_renderField: function() {
				var instance = this;

				var node = instance.get('node');

				node.val(instance.get('value'));

				var boundingBox = instance.get('boundingBox');
				var contentBox = instance.get('contentBox');

				var type = instance.get('type');

				boundingBox.addClass(getTypeClassName(type));
				node.addClass(getTypeClassName(type, 'input'));

				if (!contentBox.contains(node)) {
					if (node.inDoc()) {
						node.placeBefore(boundingBox);

						contentBox.appendChild(node);
					}
					else {
						contentBox.appendChild(node);
					}
				}

				boundingBox.removeAttribute('tabIndex');
			},

			_renderFieldHint: function() {
				var instance = this;

				var fieldHint = instance.get('fieldHint');

				if (fieldHint) {
					instance._uiSetFieldHint(fieldHint);
				}
			},

			_renderLabel: function() {
				var instance = this;

				var labelText = instance.get('labelText');

				if (labelText !== false) {
					var node = instance.get('node');
					var id = node.guid();

					labelText = instance.get('labelText');

					var labelNode = instance.get('labelNode');

					labelNode.addClass(getClassName(instance.name, 'label'));
					labelNode.setAttribute('for', id);
					labelNode.set('innerHTML', labelText);

					instance._uiSetLabelAlign(instance.get('labelAlign'));

					var contentBox = instance.get('contentBox');
					var labelAlign = instance.get('labelAlign');
					var type = instance.get('type').toLowerCase();

					var isLabelInline = REGEX_INLINE_LABEL.test(labelAlign);

					if (isLabelInline && ((type == 'checkbox') || (type == 'radio'))) {
						contentBox.append(labelNode);
					}
					else {
						contentBox.prepend(labelNode);
					}
				}
			},

			_setNodeValue: function(value) {
				var instance = this;

				instance._uiSetValue(value);

				return value;
			},

			_uiSetFieldHint: function(newVal, prevVal) {
				var instance = this;

				instance.get('fieldHintNode').set('innerHTML', newVal);
			},

			_uiSetId: function(newVal, src) {
				var instance = this;

				instance.get('node').set('id', newVal);
			},

			_uiSetLabelAlign: function(newVal, prevVal) {
				var instance = this;

				var boundingBox = instance.get('boundingBox');

				boundingBox.replaceClass(CSS_LABEL_ALIGN[prevVal], CSS_LABEL_ALIGN[newVal]);

				var action = 'removeClass';

				if (REGEX_INLINE_LABEL.test(newVal)) {
					action = 'addClass';
				}

				boundingBox[action](CSS_LABELS_INLINE);
			},

			_uiSetName: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('name', newVal);
			},

			_uiSetReadOnly: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('readOnly', newVal);
			},

			_uiSetSize: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('size', newVal);
			},

			_uiSetTabIndex: function(newVal, src) {
				var instance = this;

				instance.get('node').setAttribute('tabIndex', newVal);
			},

			_uiSetValue: function(newVal, src) {
				var instance = this;

				instance.get('node').val(newVal);
			},

			_requireAddAttr: false
		}
	}
);

A.Field = Field;<|endoftext|>


<commit_before>var inherits = require('util').inherits,
    DuplexStream = require('stream').Duplex,
    EventEmitter = require('events').EventEmitter;

var Xfer = require('xfer');

var utils = require('../utils');

var TYPE_PUB = 0x00,
    TYPE_PUB_NOSER = 0x01,
    EMPTY_READFN = function(n) {};

function Pubsub(opts) {
  if (!(this instanceof Pubsub))
    return new Pubsub();

  if (opts && typeof opts.highWaterMark === 'number')
    DuplexStream.call(this, { highWaterMark: opts.highWaterMark });
  else
    DuplexStream.call(this);

  var self = this;

  this.xfer = new Xfer(opts);
  this.xfer.on('*', function(type, stream) {
    if (!stream || (type !== TYPE_PUB && type !== TYPE_PUB_NOSER)) {
      stream && stream.resume();
      return;
    }
    self._parse(type, stream);
  });
  this.xfer.on('data', function(d) {
    self.push(d);
  });

  this.events = new EventEmitter();
  this.events._emit = this.events.emit;
  this.events.emit = function() {
    self.send.apply(self, arguments);
  };
  this.events._broadcast = 0;
  this.on('newListener', function(ev, fn) {
    if (ev === '*')
      ++self.events._broadcast;
  });
  this.on('removeListener', function(ev, fn) {
    if (ev === '*')
      --self.events._broadcast;
  });

  this.serialize = (opts && typeof opts.serialize === 'boolean'
                    ? opts.serialize
                    : true);
  this.debug = (opts && typeof opts.debug === 'function' ? opts.debug : false);
  this._read = EMPTY_READFN;
}
inherits(Pubsub, DuplexStream);

Pubsub.prototype.send = function() {
  if (arguments.length === 0)
    throw new Error('Missing event name');

  var ev = arguments[0],
      args = [],
      typeinfo;

  for (var i = 1, len = arguments.length; i < len; ++i)
    args.push(arguments[i]);

  var type, payload;

  if (args.length
      && this.serialize
      && (typeinfo = utils.serializeArgs(args)).length) {
    type = TYPE_PUB;
    payload = JSON.stringify([ev, typeinfo, args]);
  } else {
    type = TYPE_PUB;
    if (args.length)
      payload = JSON.stringify([ev, args]);
    else
      payload = JSON.stringify([ev]);
  }

  return this.xfer.send(type, payload);
};

Pubsub.prototype._write = function(chunk, encoding, cb) {
  this.xfer.write(chunk, encoding, cb);
};

Pubsub.prototype._parse = function(type, stream) {
  var buf = '', self = this;
  stream.setEncoding('utf8');
  stream.on('data', function(d) {
    buf += d;
  });
  stream.on('end', function() {
    var obj = JSON.parse(buf), ev, argslen = 0, args, typeinfo;
    ev = obj[0];
    if (obj.length === 2) {
      args = obj[1];
      argslen = args.length;
    } else if (obj.length === 3) {
      typeinfo = obj[1];
      args = obj[2];
      argslen = args.length;
    }

    if (argslen && type === TYPE_PUB && typeinfo)
      utils.unserializeArgs(typeinfo, args);

    if (argslen === 0) {
      self.events._emit(ev);
      self.events._broadcast && self.emit('*', ev);
    } else if (argslen === 1) {
      self.events._emit(ev, args[0]);
      self.events._broadcast && self.emit('*', ev, args[0]);
    } else if (argslen === 2) {
      self.events._emit(ev, args[0], args[1]);
      self.events._broadcast && self.emit('*', ev, args[0], args[1]);
    } else if (argslen === 3) {
      self.events._emit(ev, args[0], args[1], args[2]);
      self.events._broadcast && self.emit('*', ev, args[0], args[1], args[2]);
    } else {
      args.unshift(ev);
      self.events._emit.apply(self.events.emit, args);
      if (self.events._broadcast) {
        args.unshift('*');
        self.emit.apply(self.events.emit, args);
      }
    }
  });
};

module.exports = Pubsub;
<commit_msg>Pubsub: fix payload type for non-serialized payloads<commit_after>var inherits = require('util').inherits,
    DuplexStream = require('stream').Duplex,
    EventEmitter = require('events').EventEmitter;

var Xfer = require('xfer');

var utils = require('../utils');

var TYPE_PUB = 0x00,
    TYPE_PUB_NOSER = 0x01,
    EMPTY_READFN = function(n) {};

function Pubsub(opts) {
  if (!(this instanceof Pubsub))
    return new Pubsub();

  if (opts && typeof opts.highWaterMark === 'number')
    DuplexStream.call(this, { highWaterMark: opts.highWaterMark });
  else
    DuplexStream.call(this);

  var self = this;

  this.xfer = new Xfer(opts);
  this.xfer.on('*', function(type, stream) {
    if (!stream || (type !== TYPE_PUB && type !== TYPE_PUB_NOSER)) {
      stream && stream.resume();
      return;
    }
    self._parse(type, stream);
  });
  this.xfer.on('data', function(d) {
    self.push(d);
  });

  this.events = new EventEmitter();
  this.events._emit = this.events.emit;
  this.events.emit = function() {
    self.send.apply(self, arguments);
  };
  this.events._broadcast = 0;
  this.on('newListener', function(ev, fn) {
    if (ev === '*')
      ++self.events._broadcast;
  });
  this.on('removeListener', function(ev, fn) {
    if (ev === '*')
      --self.events._broadcast;
  });

  this.serialize = (opts && typeof opts.serialize === 'boolean'
                    ? opts.serialize
                    : true);
  this.debug = (opts && typeof opts.debug === 'function' ? opts.debug : false);
  this._read = EMPTY_READFN;
}
inherits(Pubsub, DuplexStream);

Pubsub.prototype.send = function() {
  if (arguments.length === 0)
    throw new Error('Missing event name');

  var ev = arguments[0],
      args = [],
      typeinfo;

  for (var i = 1, len = arguments.length; i < len; ++i)
    args.push(arguments[i]);

  var type, payload;

  if (args.length
      && this.serialize
      && (typeinfo = utils.serializeArgs(args)).length) {
    type = TYPE_PUB;
    payload = JSON.stringify([ev, typeinfo, args]);
  } else {
    type = TYPE_PUB_NOSER;
    if (args.length)
      payload = JSON.stringify([ev, args]);
    else
      payload = JSON.stringify([ev]);
  }

  return this.xfer.send(type, payload);
};

Pubsub.prototype._write = function(chunk, encoding, cb) {
  this.xfer.write(chunk, encoding, cb);
};

Pubsub.prototype._parse = function(type, stream) {
  var buf = '', self = this;
  stream.setEncoding('utf8');
  stream.on('data', function(d) {
    buf += d;
  });
  stream.on('end', function() {
    var obj = JSON.parse(buf), ev, argslen = 0, args, typeinfo;
    ev = obj[0];
    if (obj.length === 2) {
      args = obj[1];
      argslen = args.length;
    } else if (obj.length === 3) {
      typeinfo = obj[1];
      args = obj[2];
      argslen = args.length;
    }

    if (argslen && type === TYPE_PUB && typeinfo)
      utils.unserializeArgs(typeinfo, args);

    if (argslen === 0) {
      self.events._emit(ev);
      self.events._broadcast && self.emit('*', ev);
    } else if (argslen === 1) {
      self.events._emit(ev, args[0]);
      self.events._broadcast && self.emit('*', ev, args[0]);
    } else if (argslen === 2) {
      self.events._emit(ev, args[0], args[1]);
      self.events._broadcast && self.emit('*', ev, args[0], args[1]);
    } else if (argslen === 3) {
      self.events._emit(ev, args[0], args[1], args[2]);
      self.events._broadcast && self.emit('*', ev, args[0], args[1], args[2]);
    } else {
      args.unshift(ev);
      self.events._emit.apply(self.events.emit, args);
      if (self.events._broadcast) {
        args.unshift('*');
        self.emit.apply(self.events.emit, args);
      }
    }
  });
};

module.exports = Pubsub;
<|endoftext|>


<commit_before>var React = require('react/addons'),
	classnames = require('classnames');

module.exports = React.createClass({
	displayName: 'FlexBlock',
	propTypes: {
		className: React.PropTypes.string,
		height: React.PropTypes.string,
		scrollable: React.PropTypes.bool
	},

	getDefaultProps: function() {
		return {
			className: '',
			height: ''
		};
	},

	render: function() {
		var className = classnames({
			'FlexBlock': true,
			'springy-scrolling': this.props.scrollable
		}, this.props.className);

		var inlineStyle = {};

		// set height on blocks if provided
		if (this.props.height) {
			inlineStyle.height = this.props.height;

		// otherwise stretch to take up space
		} else {
			inlineStyle.WebkitBoxFlex = '1';
			inlineStyle.WebkitFlex = '1';
			inlineStyle.MozBoxFlex = '1';
			inlineStyle.MozFlex = '1';
			inlineStyle.MsFlex = '1';
			inlineStyle.flex = '1';
		}

		// allow blocks to be scrollable
		if (this.props.scrollable) {
			inlineStyle.overflowY = 'auto';
			inlineStyle.WebkitOverflowScrolling = 'touch';
		}

		return
{this.props.children}
;
	}
});
<commit_msg>afford ids on flex blocks<commit_after>var React = require('react/addons'),
	classnames = require('classnames');

module.exports = React.createClass({
	displayName: 'FlexBlock',
	propTypes: {
		id: React.PropTypes.string,
		className: React.PropTypes.string,
		height: React.PropTypes.string,
		scrollable: React.PropTypes.bool
	},

	getDefaultProps: function() {
		return {
			className: '',
			height: ''
		};
	},

	render: function() {
		var className = classnames({
			'FlexBlock': true,
			'springy-scrolling': this.props.scrollable
		}, this.props.className);

		var inlineStyle = {};

		// set height on blocks if provided
		if (this.props.height) {
			inlineStyle.height = this.props.height;

		// otherwise stretch to take up space
		} else {
			inlineStyle.WebkitBoxFlex = '1';
			inlineStyle.WebkitFlex = '1';
			inlineStyle.MozBoxFlex = '1';
			inlineStyle.MozFlex = '1';
			inlineStyle.MsFlex = '1';
			inlineStyle.flex = '1';
		}

		// allow blocks to be scrollable
		if (this.props.scrollable) {
			inlineStyle.overflowY = 'auto';
			inlineStyle.WebkitOverflowScrolling = 'touch';
		}

		return
{this.props.children}
;
	}
});
<|endoftext|>


<commit_before>import { observable, action, set } from 'mobx';
import pick from 'lodash.pick';
import { themes } from '@storybook/components';
import qs from 'qs';

export function ensurePanel(panels, selectedPanel, currentPanel) {
  const keys = Object.keys(panels);

  if (keys.indexOf(selectedPanel) >= 0) {
    return selectedPanel;
  }

  if (keys.length) {
    return keys[0];
  }
  return currentPanel;
}

const createStore = ({ provider, history }) => {
  const store = observable(
    {
      stories: [],
      storiesHash: {},
      selectedId: null,
      shortcutOptions: {
        full: false,
        nav: true,
        panel: 'right',
        enableShortcuts: true,
      },
      uiOptions: {
        name: 'STORYBOOK',
        url: 'https://github.com/storybooks/storybook',
        sortStoriesByKind: false,
        sidebarAnimations: true,
        theme: themes.normal,
      },
      customQueryParams: {},
      notifications: [
        {
          id: 'update',
          level: 2,
          link: '/settings/about',
          icon: '🎉',
          content: `There's a new version available: 4.0.0`,
        },
      ],

      selectedPanelValue: null,
      get selectedPanel() {
        return ensurePanel(this.panels, this.selectedPanelValue, this.selectedPanelValue);
      },
      set selectedPanel(value) {
        this.selectedPanelValue = value;
      },

      get panels() {
        return provider.getPanels();
      },
      get channel() {
        return provider.channel;
      },
      get mains() {
        return provider.getMains();
      },

      setOptions(changes) {
        const { selectedPanel } = changes;
        const oldOptions = this.uiOptions;
        const options = pick(changes, Object.keys(oldOptions));

        set(oldOptions, options);

        if (selectedPanel && selectedPanel !== this.selectedPanel) {
          this.selectedPanel = ensurePanel(this.panels, selectedPanel, this.selectedPanel);
        }
      },

      setShortcutsOptions(options) {
        set(this.shortcutOptions, pick(options, Object.keys(this.shortcutOptions)));
      },

      jumpToStory(direction) {
        const { storiesHash } = this;
        const { componentRoot, component } = this.urlData;
        let selectedId = component;

        const lookupList = Object.keys(storiesHash).filter(
          k => !(storiesHash[k].children || Array.isArray(storiesHash[k]))
        );

        if (!selectedId || !storiesHash[selectedId]) {
          selectedId = this.selectedId || Object.keys(storiesHash)[0];
        }
        const index = lookupList.indexOf(selectedId);

        // cannot navigate beyond fist or last
        if (index === lookupList.length - 1 && direction > 0) {
          return;
        }
        if (index === 0 && direction < 0) {
          return;
        }
        if (direction === 0) {
          return;
        }

        const result = lookupList[index + direction];

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${result}`);
          this.selectedId = result;
        }
      },

      jumpToComponent(direction) {
        const { storiesHash } = this;
        const { componentRoot, component } = this.urlData;
        let selectedId = component;
        const lookupList = Object.keys(storiesHash).filter(
          k =>
            (!storiesHash[k].children || Array.isArray(storiesHash[k])) &&
            (storiesHash[k].kind !== storiesHash[selectedId].kind || k === selectedId)
        );
        if (!selectedId || !storiesHash[selectedId]) {
          selectedId = this.selectedId || Object.keys(storiesHash)[0];
        }
        const index = lookupList.indexOf(selectedId);

        // cannot navigate beyond fist or last
        if (index === lookupList.length - 1 && direction > 0) {
          return;
        }
        if (index === 0 && direction < 0) {
          return;
        }
        if (direction === 0) {
          return;
        }

        const result = lookupList[index + direction];

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${result}`);
          this.selectedId = result;
        }
      },

      navigate(path) {
        history.navigate(`?path=${path}`);
      },

      get urlState() {
        return {
          selectedKind: this.selectedKind,
          selectedStory: this.selectedStory,
          selectedPanel: this.selectedPanel,
          full: Number(Boolean(this.shortcutOptions.full)),
          panel:
            this.shortcutOptions.panel === 'right' || this.shortcutOptions.panel === 'bottom'
              ? this.shortcutOptions.panel
              : false,
          nav: Number(Boolean(this.shortcutOptions.nav)),
          ...this.customQueryParams,
        };
      },

      get urlData() {
        const { search } = history.location;
        const { path } = qs.parse(search, { ignoreQueryPrefix: true });
        const [, p1, p2] = path.match(/\/([^/]+)\/([^/]+)?/) || [];

        const result = {};
        if (p1 && p1.match(/(components|info)/)) {
          Object.assign(result, {
            componentRoot: p1,
            component: p2,
          });
        }
        return result;
      },

      selectPanel(panelName) {
        this.selectedPanel = panelName;
      },

      setStories(stories) {
        console.log('setStories');

        this.storiesHash = stories;

        const { componentRoot, component } = this.urlData;

        // when there's no selectedId or the selectedId item doesn't exist
        // we try to resolve from state or pick the first leaf and navigate
        if (!component || !stories[component]) {
          // find first leaf
          const selectedId = this.selectedId || Object.values(stories).find(s => !s.children).path;

          if (componentRoot) {
            history.navigate(`?path=/${componentRoot}/${selectedId}`);
          }
          this.selectedId = selectedId;
        } else {
          this.selectedId = component;
        }
      },

      selectStory(kind, story, { location } = {}) {
        let selectedId;
        if (location) {
          const { storiesHash } = this;

          const { componentRoot, component } = this.urlData;
          selectedId = component;

          if (!selectedId || !storiesHash[selectedId]) {
            // skip root
            selectedId = this.selectedId || Object.keys(storiesHash)[1];
          }
          if (componentRoot) {
            history.navigate(`?path=/${componentRoot}/${component}`);
          }
          this.selectedId = selectedId;
        } else {
          throw new Error('NOT ALLOWED, must use location!');
        }
      },

      selectInCurrentKind(name) {
        const { componentRoot, component } = this.urlData;
        const selectedId = component.replace(/([^-]+)$/, name);

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${selectedId}`);
        }

        this.selectedId = selectedId;
      },

      setQueryParams(customQueryParams) {
        set(
          this.customQueryParams,
          Object.keys(customQueryParams).reduce((acc, key) => {
            if (customQueryParams[key] !== null) acc[key] = customQueryParams[key];
            return acc;
          }, {})
        );
      },
    },
    {
      setOptions: action,
      navigate: action,
      setShortcutsOptions: action,
      jumpToStory: action,
      selectPanel: action,
      setStories: action,
      selectStory: action,
      selectInCurrentKind: action,
      setQueryParams: action,
    }
  );

  return store;
};

export default createStore;
<commit_msg>CLEANUP<commit_after>import { observable, action, set } from 'mobx';
import pick from 'lodash.pick';
import { themes } from '@storybook/components';
import qs from 'qs';

export function ensurePanel(panels, selectedPanel, currentPanel) {
  const keys = Object.keys(panels);

  if (keys.indexOf(selectedPanel) >= 0) {
    return selectedPanel;
  }

  if (keys.length) {
    return keys[0];
  }
  return currentPanel;
}

const createStore = ({ provider, history }) => {
  const store = observable(
    {
      stories: [],
      storiesHash: {},
      selectedId: null,
      shortcutOptions: {
        full: false,
        nav: true,
        panel: 'right',
        enableShortcuts: true,
      },
      uiOptions: {
        name: 'STORYBOOK',
        url: 'https://github.com/storybooks/storybook',
        sortStoriesByKind: false,
        sidebarAnimations: true,
        theme: themes.normal,
      },
      customQueryParams: {},
      notifications: [
        {
          id: 'update',
          level: 2,
          link: '/settings/about',
          icon: '🎉',
          content: `There's a new version available: 4.0.0`,
        },
      ],

      selectedPanelValue: null,
      get selectedPanel() {
        return ensurePanel(this.panels, this.selectedPanelValue, this.selectedPanelValue);
      },
      set selectedPanel(value) {
        this.selectedPanelValue = value;
      },

      get panels() {
        return provider.getPanels();
      },
      get channel() {
        return provider.channel;
      },
      get mains() {
        return provider.getMains();
      },

      setOptions(changes) {
        const { selectedPanel } = changes;
        const oldOptions = this.uiOptions;
        const options = pick(changes, Object.keys(oldOptions));

        set(oldOptions, options);

        if (selectedPanel && selectedPanel !== this.selectedPanel) {
          this.selectedPanel = ensurePanel(this.panels, selectedPanel, this.selectedPanel);
        }
      },

      setShortcutsOptions(options) {
        set(this.shortcutOptions, pick(options, Object.keys(this.shortcutOptions)));
      },

      jumpToStory(direction) {
        const { storiesHash } = this;
        const { componentRoot, component } = this.urlData;
        let selectedId = component;

        const lookupList = Object.keys(storiesHash).filter(
          k => !(storiesHash[k].children || Array.isArray(storiesHash[k]))
        );

        if (!selectedId || !storiesHash[selectedId]) {
          selectedId = this.selectedId || Object.keys(storiesHash)[0];
        }
        const index = lookupList.indexOf(selectedId);

        // cannot navigate beyond fist or last
        if (index === lookupList.length - 1 && direction > 0) {
          return;
        }
        if (index === 0 && direction < 0) {
          return;
        }
        if (direction === 0) {
          return;
        }

        const result = lookupList[index + direction];

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${result}`);
          this.selectedId = result;
        }
      },

      jumpToComponent(direction) {
        const { storiesHash } = this;
        const { componentRoot, component } = this.urlData;
        let selectedId = component;
        const lookupList = Object.keys(storiesHash).filter(
          k =>
            (!storiesHash[k].children || Array.isArray(storiesHash[k])) &&
            (storiesHash[k].kind !== storiesHash[selectedId].kind || k === selectedId)
        );
        if (!selectedId || !storiesHash[selectedId]) {
          selectedId = this.selectedId || Object.keys(storiesHash)[0];
        }
        const index = lookupList.indexOf(selectedId);

        // cannot navigate beyond fist or last
        if (index === lookupList.length - 1 && direction > 0) {
          return;
        }
        if (index === 0 && direction < 0) {
          return;
        }
        if (direction === 0) {
          return;
        }

        const result = lookupList[index + direction];

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${result}`);
          this.selectedId = result;
        }
      },

      navigate(path) {
        history.navigate(`?path=${path}`);
      },

      get urlState() {
        return {
          selectedKind: this.selectedKind,
          selectedStory: this.selectedStory,
          selectedPanel: this.selectedPanel,
          full: Number(Boolean(this.shortcutOptions.full)),
          panel:
            this.shortcutOptions.panel === 'right' || this.shortcutOptions.panel === 'bottom'
              ? this.shortcutOptions.panel
              : false,
          nav: Number(Boolean(this.shortcutOptions.nav)),
          ...this.customQueryParams,
        };
      },

      get urlData() {
        const { search } = history.location;
        const { path } = qs.parse(search, { ignoreQueryPrefix: true });
        const [, p1, p2] = path.match(/\/([^/]+)\/([^/]+)?/) || [];

        const result = {};
        if (p1 && p1.match(/(components|info)/)) {
          Object.assign(result, {
            componentRoot: p1,
            component: p2,
          });
        }
        return result;
      },

      selectPanel(panelName) {
        this.selectedPanel = panelName;
      },

      setStories(storiesHash) {
        this.storiesHash = storiesHash;

        const { componentRoot, component } = this.urlData;

        // when there's no selectedId or the selectedId item doesn't exist
        // we try to resolve from state or pick the first leaf and navigate
        if (!component || !storiesHash[component]) {
          // find first leaf
          const selectedId =
            this.selectedId || Object.values(storiesHash).find(s => !s.children).path;

          if (componentRoot) {
            history.navigate(`?path=/${componentRoot}/${selectedId}`);
          }
          this.selectedId = selectedId;
        } else {
          this.selectedId = component;
        }
      },

      selectStory(kind, story, { location } = {}) {
        let selectedId;

        if (location) {
          const { storiesHash } = this;

          const { componentRoot, component } = this.urlData;
          selectedId = component;

          if (!selectedId || !storiesHash[selectedId]) {
            selectedId = this.selectedId || Object.keys(storiesHash)[0];
          }

          if (componentRoot) {
            history.navigate(`?path=/${componentRoot}/${component}`);
          }

          this.selectedId = selectedId;
        } else {
          throw new Error('NOT ALLOWED, must use location!');
        }
      },

      selectInCurrentKind(name) {
        const { componentRoot, component } = this.urlData;
        const selectedId = component.replace(/([^-]+)$/, name);

        if (componentRoot) {
          history.navigate(`?path=/${componentRoot}/${selectedId}`);
        }

        this.selectedId = selectedId;
      },

      setQueryParams(customQueryParams) {
        set(
          this.customQueryParams,
          Object.keys(customQueryParams).reduce((acc, key) => {
            if (customQueryParams[key] !== null) acc[key] = customQueryParams[key];
            return acc;
          }, {})
        );
      },
    },
    {
      setOptions: action,
      navigate: action,
      setShortcutsOptions: action,
      jumpToStory: action,
      selectPanel: action,
      setStories: action,
      selectStory: action,
      selectInCurrentKind: action,
      setQueryParams: action,
    }
  );

  return store;
};

export default createStore;
<|endoftext|>


<commit_before><commit_msg>Suggest target with oldest profile by adding a little note Add newline at the end of file<commit_after><|endoftext|>


<commit_before>"use strict";

module.exports = exports;

var fs = require('fs');
var path = require('path');
var win = process.platform == 'win32';
var existsSync = fs.existsSync || path.existsSync;
var cp = require('child_process');

// try to build up the complete path to node-gyp
/* priority:
  - node-gyp on NODE_PATH
  - node-gyp inside npm on NODE_PATH
  - node-gyp inside npm beside node exe
*/
function which_node_gyp() {
    try {
        var node_gyp_main = require.resolve('node-gyp');
        var node_gyp_bin = path.join(path.dirname(
                                     path.dirname(node_gyp_main)),
                                     'bin/node-gyp.js');
        if (existsSync(node_gyp_bin)) {
            return node_gyp_bin;
        }
    } catch (err) { }
    try {
        var npm_main = require.resolve('npm');
        var node_gyp_bin2 = path.join(path.dirname(
                                     path.dirname(npm_main)),
                                     'node_modules/node-gyp/bin/node-gyp.js');
        if (existsSync(node_gyp_bin2)) {
            return node_gyp_bin2;
        }
    } catch (err) { }
    var npm_base = path.join(path.dirname(
                             path.dirname(process.execPath)),
                             'lib/node_modules/npm/');
    var node_gyp_bin3 = path.join(npm_base,'node_modules/node-gyp/bin/node-gyp.js');
    if (existsSync(node_gyp_bin3)) {
        return node_gyp_bin3;
    }
}

module.exports.run_gyp = function(args,opts,callback) {
    var shell_cmd = '';
    var cmd_args = [];
    if (opts.runtime && opts.runtime == 'node-webkit') {
        shell_cmd = 'nw-gyp';
        if (win) shell_cmd += '.cmd';
    } else {
        var node_gyp_path = which_node_gyp();
        if (node_gyp_path) {
            shell_cmd = process.execPath;
            cmd_args.push(node_gyp_path);
        } else {
            shell_cmd = 'node-gyp';
            if (win) shell_cmd += '.cmd';
        }
    }
    var final_args = cmd_args.concat(args);
    var cmd = cp.spawn(shell_cmd, final_args, {cwd: undefined, env: process.env, stdio: [ 0, 1, 2]});
    cmd.on('error', function (err) {
        if (err) {
            return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + err + ")"));
        }
        callback(null,opts);
    });
    cmd.on('close', function (code) {
        if (code && code !== 0) {
            return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + code + ")"));
        }
        callback(null,opts);
    });
};
<commit_msg>ignore search 'node-gyp inside npm on NODE_PATH' on iojs<commit_after>"use strict";

module.exports = exports;

var fs = require('fs');
var path = require('path');
var win = process.platform == 'win32';
var existsSync = fs.existsSync || path.existsSync;
var cp = require('child_process');

// try to build up the complete path to node-gyp
/* priority:
  - node-gyp on NODE_PATH
  - node-gyp inside npm on NODE_PATH (ignore on iojs)
  - node-gyp inside npm beside node exe
*/
function which_node_gyp() {
    try {
        var node_gyp_main = require.resolve('node-gyp');
        var node_gyp_bin = path.join(path.dirname(
                                     path.dirname(node_gyp_main)),
                                     'bin/node-gyp.js');
        if (existsSync(node_gyp_bin)) {
            return node_gyp_bin;
        }
    } catch (err) { }
    if (process.execPath.indexOf('iojs') === -1) {
        try {
            var npm_main = require.resolve('npm');
            var node_gyp_bin2 = path.join(path.dirname(
                                         path.dirname(npm_main)),
                                         'node_modules/node-gyp/bin/node-gyp.js');
            if (existsSync(node_gyp_bin2)) {
                return node_gyp_bin2;
            }
        } catch (err) { }
    }
    var npm_base = path.join(path.dirname(
                             path.dirname(process.execPath)),
                             'lib/node_modules/npm/');
    var node_gyp_bin3 = path.join(npm_base,'node_modules/node-gyp/bin/node-gyp.js');
    if (existsSync(node_gyp_bin3)) {
        return node_gyp_bin3;
    }
}

module.exports.run_gyp = function(args,opts,callback) {
    var shell_cmd = '';
    var cmd_args = [];
    if (opts.runtime && opts.runtime == 'node-webkit') {
        shell_cmd = 'nw-gyp';
        if (win) shell_cmd += '.cmd';
    } else {
        var node_gyp_path = which_node_gyp();
        if (node_gyp_path) {
            shell_cmd = process.execPath;
            cmd_args.push(node_gyp_path);
        } else {
            shell_cmd = 'node-gyp';
            if (win) shell_cmd += '.cmd';
        }
    }
    var final_args = cmd_args.concat(args);
    var cmd = cp.spawn(shell_cmd, final_args, {cwd: undefined, env: process.env, stdio: [ 0, 1, 2]});
    cmd.on('error', function (err) {
        if (err) {
            return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + err + ")"));
        }
        callback(null,opts);
    });
    cmd.on('close', function (code) {
        if (code && code !== 0) {
            return callback(new Error("Failed to execute '" + shell_cmd + ' ' + final_args.join(' ') + "' (" + code + ")"));
        }
        callback(null,opts);
    });
};
<|endoftext|>