<commit_before>/*jslint node: true*/
var toArray = require('lodash.toarray');
var emojiByName = require('./emoji.json');

"use strict";

/**
 * regex to parse emoji in a string - finds emoji, e.g. :coffee:
 */
var emojiNameRegex = /:([a-zA-Z0-9_\-\+]+):/g;

/**
 * Removes colons on either side
 * of the string if present
 * @param  {string} str
 * @return {string}
 */
function stripColons (str) {
  var colonIndex = str.indexOf(':');
  if (colonIndex > -1) {
    // :emoji: (http://www.emoji-cheat-sheet.com/)
    if (colonIndex === str.length - 1) {
      str = str.substring(0, colonIndex);
      return stripColons(str);
    } else {
      str = str.substr(colonIndex + 1);
      return stripColons(str);
    }
  }

  return str;
}

/**
 * Adds colons to either side
 * of the string
 * @param {string} str
 * @return {string}
 */
function wrapColons (str) {
  return (typeof str === 'string' && str.length > 0) ? ':' + str + ':' : str;
}

/**
 * Ensure that the word is wrapped in colons
 * by only adding them, if they are not there.
 * @param {string} str
 * @return {string}
 */
function ensureColons (str) {
  return (typeof str === 'string' && str[0] !== ':') ? wrapColons(str) : str;
}

// Non spacing mark, some emoticons have them. It's the 'Variant Form',
// which provides more information so that emoticons can be rendered as
// more colorful graphics. FE0E is a unicode text version, where as FE0F
// should be rendered as a graphical version. The code gracefully degrades.
var NON_SPACING_MARK = String.fromCharCode(65039); // 65039 - '' - 0xFE0F;
var nonSpacingRegex = new RegExp(NON_SPACING_MARK, 'g')

// Remove the non-spacing-mark from the code, never send a stripped version
// to the client, as it kills graphical emoticons.
function stripNSB (code) {
  return code.replace(nonSpacingRegex, '');
};

// Reversed hash table, where as emojiByName contains a { heart: '❤' }
// dictionary emojiByCode contains { ❤: 'heart' }. The codes are normalized
// to the text version.
var emojiByCode = Object.keys(emojiByName).reduce(function(h,k) {
  h[stripNSB(emojiByName[k])] = k;
  return h;
}, {});

/**
 * Emoji namespace
 */
var Emoji = {
  emoji: emojiByName,
};

/**
 * get emoji code from name
 * @param  {string} emoji
 * @return {string}
 */
Emoji._get = function _get (emoji) {
  if (emojiByName.hasOwnProperty(emoji)) {
    return emojiByName[emoji];
  }

  return ensureColons(emoji);
};

/**
 * get emoji code from :emoji: string or name
 * @param  {string} emoji
 * @return {string}
 */
Emoji.get = function get (emoji) {
  emoji = stripColons(emoji);

  return Emoji._get(emoji);
};

/**
 * find the emoji by either code or name
 * @param {string} nameOrCode The emoji to find, either `coffee`, `:coffee:` or `☕`;
 * @return {object}
 */
Emoji.find = function find (nameOrCode) {
  return Emoji.findByName(nameOrCode) || Emoji.findByCode(nameOrCode);
};

/**
 * find the emoji by name
 * @param {string} name The emoji to find either `coffee` or `:coffee:`;
 * @return {object}
 */
Emoji.findByName = function findByName (name) {
  var stripped = stripColons(name);
  var emoji = emojiByName[stripped];

  return emoji ? ({ emoji: emoji, key: stripped }) : undefined;
};

/**
 * find the emoji by code (emoji)
 * @param {string} code The emoji to find; for example `☕` or `☔`
 * @return {object}
 */
Emoji.findByCode = function findByCode (code) {
  var stripped = stripNSB(code);
  var name = emojiByCode[stripped];

  // lookup emoji to ensure the Variant Form is returned
  return name ? ({ emoji: emojiByName[name], key: name }) : undefined;
};

/**
 * Check if an emoji is known by this library
 * @param {string} nameOrCode The emoji to validate, either `coffee`, `:coffee:` or `☕`;
 * @return {object}
 */
Emoji.hasEmoji = function hasEmoji (nameOrCode) {
  return Emoji.hasEmojiByName(nameOrCode) || Emoji.hasEmojiByCode(nameOrCode);
};

/**
 * Check if an emoji with given name is known by this library
 * @param {string} name The emoji to validate either `coffee` or `:coffee:`;
 * @return {object}
 */
Emoji.hasEmojiByName = function hasEmojiByName (name) {
  var result = Emoji.findByName(name);
  return !!result && result.key === stripColons(name);
};

/**
 * Check if a given emoji is known by this library
 * @param {string} code The emoji to validate; for example `☕` or `☔`
 * @return {object}
 */
Emoji.hasEmojiByCode = function hasEmojiByCode (code) {
  var result = Emoji.findByCode(code);
  return !!result && stripNSB(result.emoji) === stripNSB(code);
};

/**
 * get emoji name from code
 * @param  {string} emoji
 * @param  {boolean} includeColons should the result include the ::
 * @return {string}
 */
Emoji.which = function which (emoji_code, includeColons) {
  var code = stripNSB(emoji_code);
  var word = emojiByCode[code];

  return includeColons ? wrapColons(word) : word;
};

/**
 * emojify a string (replace :emoji: with an emoji)
 * @param  {string} str
 * @param  {function} on_missing (gets emoji name without :: and returns a proper emoji if no emoji was found)
 * @param  {function} format (wrap the returned emoji in a custom element)
 * @return {string}
 */
Emoji.emojify = function emojify (str, on_missing, format) {
  if (!str) return '';

  return str.split(emojiNameRegex) // parse emoji via regex
            .map(function parseEmoji(s, i) {
              // every second element is an emoji, e.g. "test :fast_forward:" -> [ "test ", "fast_forward" ]
              if (i % 2 === 0) return s;
              var emoji = Emoji._get(s);
              var isMissing = emoji.indexOf(':') > -1;

              if (isMissing && typeof on_missing === 'function') {
                return on_missing(s);
              }

              if (!isMissing && typeof format === 'function') {
                return format(emoji, s);
              }

              return emoji;
            })
            .join('') // convert back to string
  ;
};

/**
 * return a random emoji
 * @return {string}
 */
Emoji.random = function random () {
  var emojiKeys = Object.keys(emojiByName);
  var randomIndex = Math.floor(Math.random() * emojiKeys.length);
  var key = emojiKeys[randomIndex];
  var emoji = Emoji._get(key);
  return { key: key, emoji: emoji };
}

/**
 *  return an collection of potential emoji matches
 *  @param {string} str
 *  @return {Array.<Object>}
 */
Emoji.search = function search (str) {
  var emojiKeys = Object.keys(emojiByName);
  var matcher = stripColons(str)
  var matchingKeys = emojiKeys.filter(function(key) {
    return key.toString().indexOf(matcher) === 0;
  });
  return matchingKeys.map(function(key) {
    return {
      key: key,
      emoji: Emoji._get(key),
    };
  });
}

/**
 * unemojify a string (replace emoji with :emoji:)
 * @param  {string} str
 * @return {string}
 */
Emoji.unemojify = function unemojify (str) {
  if (!str) return '';
  var words = toArray(str);

  return words.map(function(word) {
    return Emoji.which(word, true) || word;
  }).join('');
};

module.exports = Emoji;
<commit_msg>Add support for Emoji.replace and Emoji.strip<commit_after>/*jslint node: true*/
var toArray = require('lodash.toarray');
var emojiByName = require('./emoji.json');

"use strict";

/**
 * regex to parse emoji in a string - finds emoji, e.g. :coffee:
 */
var emojiNameRegex = /:([a-zA-Z0-9_\-\+]+):/g;

/**
 * regex to trim whitespace
 * use instead of String.prototype.trim() for IE8 supprt
 */
var trimSpaceRegex = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

/**
 * Removes colons on either side
 * of the string if present
 * @param  {string} str
 * @return {string}
 */
function stripColons (str) {
  var colonIndex = str.indexOf(':');
  if (colonIndex > -1) {
    // :emoji: (http://www.emoji-cheat-sheet.com/)
    if (colonIndex === str.length - 1) {
      str = str.substring(0, colonIndex);
      return stripColons(str);
    } else {
      str = str.substr(colonIndex + 1);
      return stripColons(str);
    }
  }

  return str;
}

/**
 * Adds colons to either side
 * of the string
 * @param {string} str
 * @return {string}
 */
function wrapColons (str) {
  return (typeof str === 'string' && str.length > 0) ? ':' + str + ':' : str;
}

/**
 * Ensure that the word is wrapped in colons
 * by only adding them, if they are not there.
 * @param {string} str
 * @return {string}
 */
function ensureColons (str) {
  return (typeof str === 'string' && str[0] !== ':') ? wrapColons(str) : str;
}

// Non spacing mark, some emoticons have them. It's the 'Variant Form',
// which provides more information so that emoticons can be rendered as
// more colorful graphics. FE0E is a unicode text version, where as FE0F
// should be rendered as a graphical version. The code gracefully degrades.
var NON_SPACING_MARK = String.fromCharCode(65039); // 65039 - '' - 0xFE0F;
var nonSpacingRegex = new RegExp(NON_SPACING_MARK, 'g')

// Remove the non-spacing-mark from the code, never send a stripped version
// to the client, as it kills graphical emoticons.
function stripNSB (code) {
  return code.replace(nonSpacingRegex, '');
};

// Reversed hash table, where as emojiByName contains a { heart: '❤' }
// dictionary emojiByCode contains { ❤: 'heart' }. The codes are normalized
// to the text version.
var emojiByCode = Object.keys(emojiByName).reduce(function(h,k) {
  h[stripNSB(emojiByName[k])] = k;
  return h;
}, {});

/**
 * Emoji namespace
 */
var Emoji = {
  emoji: emojiByName,
};

/**
 * get emoji code from name
 * @param  {string} emoji
 * @return {string}
 */
Emoji._get = function _get (emoji) {
  if (emojiByName.hasOwnProperty(emoji)) {
    return emojiByName[emoji];
  }

  return ensureColons(emoji);
};

/**
 * get emoji code from :emoji: string or name
 * @param  {string} emoji
 * @return {string}
 */
Emoji.get = function get (emoji) {
  emoji = stripColons(emoji);

  return Emoji._get(emoji);
};

/**
 * find the emoji by either code or name
 * @param {string} nameOrCode The emoji to find, either `coffee`, `:coffee:` or `☕`;
 * @return {object}
 */
Emoji.find = function find (nameOrCode) {
  return Emoji.findByName(nameOrCode) || Emoji.findByCode(nameOrCode);
};

/**
 * find the emoji by name
 * @param {string} name The emoji to find either `coffee` or `:coffee:`;
 * @return {object}
 */
Emoji.findByName = function findByName (name) {
  var stripped = stripColons(name);
  var emoji = emojiByName[stripped];

  return emoji ? ({ emoji: emoji, key: stripped }) : undefined;
};

/**
 * find the emoji by code (emoji)
 * @param {string} code The emoji to find; for example `☕` or `☔`
 * @return {object}
 */
Emoji.findByCode = function findByCode (code) {
  var stripped = stripNSB(code);
  var name = emojiByCode[stripped];

  // lookup emoji to ensure the Variant Form is returned
  return name ? ({ emoji: emojiByName[name], key: name }) : undefined;
};

/**
 * Check if an emoji is known by this library
 * @param {string} nameOrCode The emoji to validate, either `coffee`, `:coffee:` or `☕`;
 * @return {object}
 */
Emoji.hasEmoji = function hasEmoji (nameOrCode) {
  return Emoji.hasEmojiByName(nameOrCode) || Emoji.hasEmojiByCode(nameOrCode);
};

/**
 * Check if an emoji with given name is known by this library
 * @param {string} name The emoji to validate either `coffee` or `:coffee:`;
 * @return {object}
 */
Emoji.hasEmojiByName = function hasEmojiByName (name) {
  var result = Emoji.findByName(name);
  return !!result && result.key === stripColons(name);
};

/**
 * Check if a given emoji is known by this library
 * @param {string} code The emoji to validate; for example `☕` or `☔`
 * @return {object}
 */
Emoji.hasEmojiByCode = function hasEmojiByCode (code) {
  var result = Emoji.findByCode(code);
  return !!result && stripNSB(result.emoji) === stripNSB(code);
};

/**
 * get emoji name from code
 * @param  {string} emoji
 * @param  {boolean} includeColons should the result include the ::
 * @return {string}
 */
Emoji.which = function which (emoji_code, includeColons) {
  var code = stripNSB(emoji_code);
  var word = emojiByCode[code];

  return includeColons ? wrapColons(word) : word;
};

/**
 * emojify a string (replace :emoji: with an emoji)
 * @param  {string} str
 * @param  {function} on_missing (gets emoji name without :: and returns a proper emoji if no emoji was found)
 * @param  {function} format (wrap the returned emoji in a custom element)
 * @return {string}
 */
Emoji.emojify = function emojify (str, on_missing, format) {
  if (!str) return '';

  return str.split(emojiNameRegex) // parse emoji via regex
            .map(function parseEmoji(s, i) {
              // every second element is an emoji, e.g. "test :fast_forward:" -> [ "test ", "fast_forward" ]
              if (i % 2 === 0) return s;
              var emoji = Emoji._get(s);
              var isMissing = emoji.indexOf(':') > -1;

              if (isMissing && typeof on_missing === 'function') {
                return on_missing(s);
              }

              if (!isMissing && typeof format === 'function') {
                return format(emoji, s);
              }

              return emoji;
            })
            .join('') // convert back to string
  ;
};

/**
 * return a random emoji
 * @return {string}
 */
Emoji.random = function random () {
  var emojiKeys = Object.keys(emojiByName);
  var randomIndex = Math.floor(Math.random() * emojiKeys.length);
  var key = emojiKeys[randomIndex];
  var emoji = Emoji._get(key);
  return { key: key, emoji: emoji };
}

/**
 *  return an collection of potential emoji matches
 *  @param {string} str
 *  @return {Array.<Object>}
 */
Emoji.search = function search (str) {
  var emojiKeys = Object.keys(emojiByName);
  var matcher = stripColons(str)
  var matchingKeys = emojiKeys.filter(function(key) {
    return key.toString().indexOf(matcher) === 0;
  });
  return matchingKeys.map(function(key) {
    return {
      key: key,
      emoji: Emoji._get(key),
    };
  });
}

/**
 * unemojify a string (replace emoji with :emoji:)
 * @param  {string} str
 * @return {string}
 */
Emoji.unemojify = function unemojify (str) {
  if (!str) return '';
  var words = toArray(str);

  return words.map(function(word) {
    return Emoji.which(word, true) || word;
  }).join('');
};

/**
 * replace emojis with replacement value
 * @param {string} str
 * @param {function|string} the string or callback function to replace the emoji with
 * @param {boolean} should trailing whitespaces be cleaned? Defaults false
 * @return {string}
 */
Emoji.replace = function replace (str, replacement, cleanSpaces) {
  if (!str) return '';

  var replace = typeof replacement === 'function' ? replacement : function() { return replacement; };
  var words = toArray(str);

  var replaced = words.map(function(word, idx) {
    var emoji = Emoji.findByCode(word);

    if (emoji && cleanSpaces && words[idx + 1] === ' ') {
      words[idx + 1] = '';
    }

    return emoji ? replace(emoji) : word;
  }).join('');

  return cleanSpaces ? replaced.replace(trimSpaceRegex, '') : replaced;
};

/**
 * remove all emojis from a string
 * @param {string} str
 * @return {string}
 */
Emoji.strip = function strip (str) {
  return Emoji.replace(str, '', true);
};

module.exports = Emoji;
<|endoftext|>


<commit_before>'use strict';

var async = require('async');
var us = require('underscore-node');
var db = require('mongojs')('mongodb://localhost:27017/martenblog', ['entry', 'topic']);

function Entry() {
  var that = this;

  // private
  // if id is null, then a new one will be generated.
  var next_id = function(coll, id, cb) {
    if(id) {
      cb(id);
    } else {
      db.collection(coll).find({}, {_id: 1}).sort({_id: -1}).limit(1, function(err, e_arr) {
        cb(e_arr[0]._id + 1);
      });
    }
  };

 var created_at = function() {
    if('date' in that) {
      var re = that.date.length === 10 ? /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ : /^(\d\d\d\d)(\d\d)(\d\d)$/;
      var m = re.exec(that.date);
      if(m) {
        if(m[4] === undefined) {
          return(new Date(m[1], m[2], m[3]));
        } else {
          return(new Date(m[1], m[2], m[3], m[4], m[5]));
        }
      } else {
        return(new Date());
      }
    } else {
      return(new Date());
    }
  };

  var grab_extant_topics = function(cb) {
    if('_id' in that) {
      db.entry.findOne({_id: that._id}, function(err, e) {
        cb(e.topic_ids || {});
      });
    } else {
      cb([]);
    }
  };

  var arrange_topics = function(entry_id, cb) {
    var entry_ids;
    grab_extant_topics(function(topic_ids) {
      async.eachSeries(that.topic, function(t, t_cb) {
        db.topic.findOne({topic: t}, function(err, t_obj) {
          if(err) { throw(err); }
          next_id('topic', t_obj ? t_obj._id : undefined, function(id) {
            topic_ids.push(id);
            entry_ids = t_obj ? t_obj.entry_ids : [];
            entry_ids.push(entry_id);
            db.topic.save({_id: id, topic: t, entry_ids: us.uniq(entry_ids)}, function() {
              t_cb(null);
            });
          });
        });
      }, function(err) {
        if(err) { throw(err); }
        that.add('topic', topic_ids);
        cb(that);  // I suppose I'll still be in this context.
      });
    });
  };

  // privileged
  this.regurgitate = function(cb) {
    next_id('entry', this._id, function(id) {
      arrange_topics(id, function(that) {
        var doc = {_id: id, created_at: created_at(), entry: that.entry, subject: that.subject, topic_ids: that.topic, user_id: 1};
        db.entry.save(doc, function() {
          cb();
        });
      });
    });
  };
}
Entry.prototype.add = function(key, value) {
  this[key] = value;
};
Entry.prototype.delete = function(key) {
  delete this[key];
};

module.exports = new Entry();
<commit_msg>well, maybe that was not v1.0<commit_after>'use strict';

var async = require('async');
var us = require('underscore-node');
var db = require('mongojs')('mongodb://localhost:27017/martenblog', ['entry', 'topic']);

function Entry() {
  var that = this;

  // private
  // if id is null, then a new one will be generated.
  var next_id = function(coll, id, cb) {
    if(id) {
      cb(id);
    } else {
      db.collection(coll).find({}, {_id: 1}).sort({_id: -1}).limit(1, function(err, e_arr) {
        cb(e_arr[0]._id + 1);
      });
    }
  };

 var created_at = function() {
    if('date' in that) {
      var re = that.date.length === 10 ? /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ : /^(\d\d\d\d)(\d\d)(\d\d)$/;
      var m = re.exec(that.date);
      if(m) {
        if(m[4] === undefined) {
          return(new Date(m[1], m[2], m[3]));
        } else {
          return(new Date(m[1], m[2], m[3], m[4], m[5]));
        }
      } else {
        return(new Date());
      }
    } else {
      return(new Date());
    }
  };

  var grab_extant_topics = function(cb) {
    if('_id' in that) {
      db.entry.findOne({_id: that._id}, function(err, e) {
        cb(e.topic_ids || []);
      });
    } else {
      cb([]);
    }
  };

  var arrange_topics = function(entry_id, cb) {
    var entry_ids;
    grab_extant_topics(function(topic_ids) {
      async.eachSeries(that.topic, function(t, t_cb) {
        db.topic.findOne({topic: t}, function(err, t_obj) {
          if(err) { throw(err); }
          next_id('topic', t_obj ? t_obj._id : undefined, function(id) {
            topic_ids.push(id);
            entry_ids = t_obj ? t_obj.entry_ids : [];
            entry_ids.push(entry_id);
            db.topic.save({_id: id, topic: t, entry_ids: us.uniq(entry_ids)}, function() {
              t_cb(null);
            });
          });
        });
      }, function(err) {
        if(err) { throw(err); }
        that.add('topic', us.uniq(topic_ids));
        cb(that);  // I suppose I'll still be in this context.
      });
    });
  };

  // privileged
  this.regurgitate = function(cb) {
    next_id('entry', this._id, function(id) {
      arrange_topics(id, function(that) {
        var doc = {_id: id, created_at: created_at(), entry: that.entry, subject: that.subject, topic_ids: that.topic, user_id: 1};
        db.entry.update({_id: doc._id}, doc, {upsert: true}, function() {
          cb();
        });
      });
    });
  };
}
Entry.prototype.add = function(key, value) {
  this[key] = value;
};
Entry.prototype.delete = function(key) {
  delete this[key];
};

module.exports = new Entry();
<|endoftext|>


<commit_before>define([
  "./vars"
], function (vars) {
  "use strict";

  var addStyles = function (/*String*/text, /*String|Node?*/id) {
    // summary:
    //    Creates a style element and adds it to the document head.
    // text: String
    // id: [optional] String|Node
    //    Id to be used on the style node or the node to which the styles should be added.
    var styles = document.createElement("style"),
      existingStyles;

    if (id) {
      if (typeof id === "string") {
        existingStyles = document.getElementById(id);

        if (existingStyles) {
          document.head.removeChild(existingStyles);
        }

        styles.setAttribute("id", id);
      } else {
        styles = id;
        document.head.removeChild(styles);
        styles.innerHTML = "";
      }
    }

    styles.innerHTML = text;

    document.head.appendChild(styles);
  };

  return {
    addStyles: addStyles,
    parse: function (/*TPS.css*/structure, /*Object?*/opts) {
      // summary:
      //    Parse a css structure into CSS text.
      // structure: TuparseShakur.css
      // options: [optional] Object
      // returns:
      //    CSS text
      var variables = structure.variables,
        styles = structure.styles,
        tags = vars.tags,
        selectorParse = /(\&)?(\>\s*\w+([\w\d]+)?)?(\:\w+([\w\d]+)?)?(\.\w+([\w\d]+)?((\-[\w\d]+)+)?)?(\[([^\]=]+)=?['"]?([^\]'"]*)['"]?\])?/g,
        text = "",
        options = opts || {},
        createSelector, parse, selectors, i, total;

      createSelector = function (/*String*/selector) {
        // summary:
        //    Creates a selector container class instance.
        //    This allows delayed adding of rules and children to the
        //    selector prior to needing to perform the final build.
        // returns:
        //    CSS text.
        var rules = [],
          children = [],
          parent = null;

        return {
          build: function () {
            // summary:
            //    Builds selector into corresponding css text.
            // returns:
            //    CSS text.
            var text = "",
              i, total;

            if (rules.length) {
              text += this.getQualifiedSelector() + "{" + rules.join("") + "}\n";
            }

            for (i = 0, total = children.length; i < total; i++) {
              text += children[i].build();
            }

            return text;
          },
          addRule: function (/*String*/property, /*String*/value) {
            // summary:
            //    Add a rule to the selector.
            // property: String
            // value: String
            rules.push(property.replace("_", "-") + ":" + (typeof value === "string" ? value.replace(/\@[^\s]+/g, function (match) {
              var value = variables[match];

              return value || match;
            }) : value) + ";");
          },
          addChild: function (/*Selector*/child) {
            // summary:
            //    Add a child to the selector.
            // child: Selector
            child.addParent(this);
            children.push(child);
          },
          addParent: function (/*Selector*/parentSelector) {
            // summary:
            //    Add a parent to the selector.
            parent = parentSelector;
          },
          getQualifiedSelector: function () {
            // summary:
            //    Get the Selector's full selector.  Includes all parent selectors.
            // returns:
            //    Fully qualified selector.
            var prefix = selector.substring(0, 1),
              modifiedSelector, qualifiedSelector;

            if (prefix === ":") {
              qualifiedSelector = parent ? parent.getQualifiedSelector() + selector : selector;
            } else if (prefix === "&") {
              modifiedSelector = selector.substring(1, selector.length);
              qualifiedSelector = parent ? parent.getQualifiedSelector() + modifiedSelector : modifiedSelector;
            } else {
              qualifiedSelector = parent ? parent.getQualifiedSelector() + " " + selector : selector;
            }

            return qualifiedSelector;
          }
        };
      };

      parse = function (/*TPS.selector*/structure, /*Selector*/parentSelector) {
        // summary:
        //    Parse a Selector and its rules/children.
        // structure: TPS.selector
        // parentSelector: Selector
        // returns:
        //    Selector[]
        var selectors = [],
          tag, selector, property;

        for (property in structure) {
          if (structure.hasOwnProperty(property)) {
            tag = property.replace(selectorParse, "");

            if (tag !== "" && !tags[tag]) {
              parentSelector.addRule(property, structure[property]);
            } else {
              selector = createSelector(property);
              selectors.push(selector);
              if (parentSelector) {
                parentSelector.addChild(selector);
              }

              parse(structure[property], selector);
            }
          }
        }

        return selectors;
      };

      selectors = parse(styles);
      for (i = 0, total = selectors.length; i < total; i++) {
        text += selectors[i].build();
      }

      if (options.addStyles) {
        this.addStyles(text, options.id);
      }

      if (options.appendWhenDone) {
        addStyles(text, options.stylesId || options.stylesNode);
      }

      return text;
    }
  };
});<commit_msg>Revert last change -- unnecessary<commit_after>define([
  "./vars"
], function (vars) {
  "use strict";

  var addStyles = function (/*String*/text, /*String|Node?*/id) {
    // summary:
    //    Creates a style element and adds it to the document head.
    // text: String
    // id: [optional] String|Node
    //    Id to be used on the style node or the node to which the styles should be added.
    var styles = document.createElement("style"),
      existingStyles;

    if (id) {
      if (typeof id === "string") {
        existingStyles = document.getElementById(id);

        if (existingStyles) {
          document.head.removeChild(existingStyles);
        }

        styles.setAttribute("id", id);
      } else {
        styles = id;
      }
    }

    styles.innerHTML = text;

    document.head.appendChild(styles);
  };

  return {
    addStyles: addStyles,
    parse: function (/*TPS.css*/structure, /*Object?*/opts) {
      // summary:
      //    Parse a css structure into CSS text.
      // structure: TuparseShakur.css
      // options: [optional] Object
      // returns:
      //    CSS text
      var variables = structure.variables,
        styles = structure.styles,
        tags = vars.tags,
        selectorParse = /(\&)?(\>\s*\w+([\w\d]+)?)?(\:\w+([\w\d]+)?)?(\.\w+([\w\d]+)?((\-[\w\d]+)+)?)?(\[([^\]=]+)=?['"]?([^\]'"]*)['"]?\])?/g,
        text = "",
        options = opts || {},
        createSelector, parse, selectors, i, total;

      createSelector = function (/*String*/selector) {
        // summary:
        //    Creates a selector container class instance.
        //    This allows delayed adding of rules and children to the
        //    selector prior to needing to perform the final build.
        // returns:
        //    CSS text.
        var rules = [],
          children = [],
          parent = null;

        return {
          build: function () {
            // summary:
            //    Builds selector into corresponding css text.
            // returns:
            //    CSS text.
            var text = "",
              i, total;

            if (rules.length) {
              text += this.getQualifiedSelector() + "{" + rules.join("") + "}\n";
            }

            for (i = 0, total = children.length; i < total; i++) {
              text += children[i].build();
            }

            return text;
          },
          addRule: function (/*String*/property, /*String*/value) {
            // summary:
            //    Add a rule to the selector.
            // property: String
            // value: String
            rules.push(property.replace("_", "-") + ":" + (typeof value === "string" ? value.replace(/\@[^\s]+/g, function (match) {
              var value = variables[match];

              return value || match;
            }) : value) + ";");
          },
          addChild: function (/*Selector*/child) {
            // summary:
            //    Add a child to the selector.
            // child: Selector
            child.addParent(this);
            children.push(child);
          },
          addParent: function (/*Selector*/parentSelector) {
            // summary:
            //    Add a parent to the selector.
            parent = parentSelector;
          },
          getQualifiedSelector: function () {
            // summary:
            //    Get the Selector's full selector.  Includes all parent selectors.
            // returns:
            //    Fully qualified selector.
            var prefix = selector.substring(0, 1),
              modifiedSelector, qualifiedSelector;

            if (prefix === ":") {
              qualifiedSelector = parent ? parent.getQualifiedSelector() + selector : selector;
            } else if (prefix === "&") {
              modifiedSelector = selector.substring(1, selector.length);
              qualifiedSelector = parent ? parent.getQualifiedSelector() + modifiedSelector : modifiedSelector;
            } else {
              qualifiedSelector = parent ? parent.getQualifiedSelector() + " " + selector : selector;
            }

            return qualifiedSelector;
          }
        };
      };

      parse = function (/*TPS.selector*/structure, /*Selector*/parentSelector) {
        // summary:
        //    Parse a Selector and its rules/children.
        // structure: TPS.selector
        // parentSelector: Selector
        // returns:
        //    Selector[]
        var selectors = [],
          tag, selector, property;

        for (property in structure) {
          if (structure.hasOwnProperty(property)) {
            tag = property.replace(selectorParse, "");

            if (tag !== "" && !tags[tag]) {
              parentSelector.addRule(property, structure[property]);
            } else {
              selector = createSelector(property);
              selectors.push(selector);
              if (parentSelector) {
                parentSelector.addChild(selector);
              }

              parse(structure[property], selector);
            }
          }
        }

        return selectors;
      };

      selectors = parse(styles);
      for (i = 0, total = selectors.length; i < total; i++) {
        text += selectors[i].build();
      }

      if (options.addStyles) {
        this.addStyles(text, options.id);
      }

      if (options.appendWhenDone) {
        addStyles(text, options.stylesId || options.stylesNode);
      }

      return text;
    }
  };
});<|endoftext|>


<commit_before>/**
 * Feeds API requests and definitions for Amazon's MWS web services.
 * For information on using, please see examples folder.
 *
 * @author Jakub Gabčo
 */

var mws = require('./mws');
var _ = require('underscore');

module.exports = {

    /**
     * @abstract Enumerates all the feed types that are available through the Feeds API section.
     * @description The FeedType enumeration includes all the feed types that you can submit using the operations in Amazon MWS Feeds API section.
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_FeedType.html
     */
    FeedTypes: [
        '_POST_PRODUCT_DATA_', '_POST_PRODUCT_RELATIONSHIP_DATA_', '_POST_ITEM_DATA_', '_POST_PRODUCT_OVERRIDES_DATA_', '_POST_PRODUCT_IMAGE_DATA_',
        '_POST_PRODUCT_PRICING_DATA_', '_POST_INVENTORY_AVAILABILITY_DATA_', '_POST_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_ORDER_FULFILLMENT_DATA_',
        '_POST_FULFILLMENT_ORDER_REQUEST_DATA_', '_POST_FULFILLMENT_ORDER_CANCELLATION', '_POST_PAYMENT_ADJUSTMENT_DATA_', '_POST_INVOICE_CONFIRMATION_DATA_',
        '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_FLAT_FILE_FULFILLMENT_DATA_',
        '_POST_FLAT_FILE_FBA_CREATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_FBA_UPDATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_',
        '_POST_FLAT_FILE_INVOICE_CONFIRMATION_DATA_', '_POST_FLAT_FILE_INVLOADER_DATA_', '_POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA_',
        '_POST_FLAT_FILE_BOOKLOADER_DATA_', '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_PRICEANDQUANTITYONLY', '_POST_UIEE_BOOKLOADER_DATA_'
    ],

    /**
     * @abstract Enumerates all the feed processing status values that are available through the Feeds API section.
     * @description The FeedProcessingStatus enumeration describes the processing status of a submitted feed.
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_FeedProcessingStatus.html
     */
    FeedProcessingStatus: [
        '_AWAITING_ASYNCHRONOUS_REPLY_', '_CANCELLED_', '_DONE_', '_IN_PROGRESS_', '_IN_SAFETY_NET_', '_SUBMITTED_', '_UNCONFIRMED_'
    ],

    /**
     * @abstract
     * @description
     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see
     */
    CancelFeedSubmissions: function (config, params, xml, cb) {

    },

    /**
     * @abstract Returns a list of all feed submissions submitted in the previous 90 days.
     * @description

     The GetFeedSubmissionList operation returns a list of feed submissions submitted in the previous 90 days that match the query parameters. Use this operation to determine the status of a feed submission by passing in the FeedProcessingId that was returned by the SubmitFeed operation.

     The GetFeedSubmissionList request can return a maximum of 100 results. If there are additional results to return, HasNext is returned in the response with a true value. To retrieve all the results, you can pass the value of the NextToken parameter to the GetFeedSubmissionListByNextToken operation and repeat until HasNext is false.

     The GetFeedSubmissionList operation has a maximum request quota of 10 and a restore rate of one request every 45 seconds. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional: FeedSubmissionIdList,MaxCount, FeedTypeList, FeedProcessingStatusList, SubmittedFromDate, SubmittedToDate
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_GetFeedSubmissionList.html
     */
    GetFeedSubmissionList: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }
        var parameters = {};
        if (_.has(params, 'FeedSubmissionIdList')) {
            if (!_.isEmpty(params['FeedSubmissionIdList'])) {
                for (var key in params['FeedSubmissionIdList']) {
                    if (/FeedSubmissionIdLis.Id.[1-9]+/.test(key)) {
                        parameters[key] = params['FeedSubmissionIdList'][key];
                    }
                }
            }
        }

        if (_.has(params, 'MaxCount')) {
            parameters['MaxCount'] = params['MaxCount'];
        }

        if (_.has(params, 'FeedTypeList')) {
            for (var key in params['FeedTypeList']) {
                if (/FeedTypeList.Type.[0-9]+/.test(key) && _.contains(this.FeedTypes, params['FeedTypeList'][key])) {
                    parameters[key] = params['FeedTypeList'][key];
                }
            }
        }

        if (_.has(params, 'FeedProcessingStatusList')) {
            for (var key in params['FeedProcessingStatusList']) {
                if (/FeedProcessingStatusList.Status.[0-9]+/.test(key) && _.has(this.FeedProcessingStatus, params['FeedProcessingStatusList'][key])) {
                    parameters[key] = params['FeedProcessingStatusList'][key];
                }
            }
        }

        if (_.has(params, 'SubmittedFromDate')) {
            parameters['SubmittedFromDate'] = params['SubmittedFromDate'];
        }

        if (_.has(params, 'SubmittedToDate')) {
            parameters['SubmittedToDate'] = params['SubmittedToDate'];
        }

        mws.createRequest({
            'action': 'GetFeedSubmissionList',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });
    },

    /**
     * @abstract
     * @description
     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see
     */
    GetFeedSubmissionListByNextToken: function (config, params, xml, cb) {

    },

    /**
     * @abstract
     * @description
     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see
     */
    GetFeedSubmissionCount: function (config, params, xml, cb) {

    },

    /**
     * @abstract
     * @description
     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see
     */
    GetFeedSubmissionResult: function (config, params, xml, cb) {

    },

    /**
     * @abstract Uploads a feed for processing by Amazon MWS.
     * @description  The SubmitFeed operation uploads a file and any necessary metadata for processing. Note that you must calculate a Content-MD5 header for the submitted file. For more information about creating a Content-MD5 header, see What you should know about the Amazon MWS Feeds API section.

     The SubmitFeed operation has a maximum request quota of 15 and a restore rate of one request every two minutes. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     Feed size is limited to 2 GB (231-1, or 2,147,483,647 bytes) per feed. If you have a large amount of data to submit, you should submit feeds smaller than the feed size limit by breaking up the data, or submit the feeds over a period of time. For optimal performance, a good practice is to submit feeds with a size limit of 30,000 records/items, or submit feeds over a period of time, such as every few hours.
     * @param {Object} config Configuration file
     * @param {Object} params Required: FeedType Optional: MarketplaceIdList, PurgeAndReplace
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_SubmitFeed.html
     */
    SubmitFeed: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }

        if (!_.has(params, 'FeedType')) {
            cb({
                'Error': 'You need to pass parameter FeedType'
            }, null);
            return;
        }
        if (!_.contains(this.FeedTypes, params['FeedType'])) {
            cb({
                'Error': 'FeedType must be one of the FeedTypes listed in AmazonDocs'
            }, null);
            return;
        }

        parameters = {
            'FeedType': params['FeedType']
        };

        if (_.has(params, 'MarketplaceIdList')) {
            var counter = 1;
            for (var key in params['MarketplaceIdList']) {
                if (/MarketplaceIdList.Id.[1-9]+/.test(key)) {
                    parameters[key] = params['MarketplaceIdList'][key];
                }
                counter++;
                if (counter > 20) {
                    break;
                }
            }
        }
        if (_.has(params, 'PurgeAndReplace')) {
            if (typeof params['PurgeAndReplace'] === 'boolean') {
                parameters['PurgeAndReplace'] = params['PurgeAndReplace'];
            }
        }

        mws.createRequest({
            'action': 'SubmitFeed',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });

    }

};
<commit_msg>Added all unimported functions from Feeds endpoint to project.<commit_after>/**
 * Feeds API requests and definitions for Amazon's MWS web services.
 * For information on using, please see examples folder.
 *
 * @author Jakub Gabčo
 */

var mws = require('./mws');
var _ = require('underscore');

module.exports = {

    /**
     * @abstract Enumerates all the feed types that are available through the Feeds API section.
     * @description The FeedType enumeration includes all the feed types that you can submit using the operations in Amazon MWS Feeds API section.
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_FeedType.html
     */
    FeedTypes: [
        '_POST_PRODUCT_DATA_', '_POST_PRODUCT_RELATIONSHIP_DATA_', '_POST_ITEM_DATA_', '_POST_PRODUCT_OVERRIDES_DATA_', '_POST_PRODUCT_IMAGE_DATA_',
        '_POST_PRODUCT_PRICING_DATA_', '_POST_INVENTORY_AVAILABILITY_DATA_', '_POST_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_ORDER_FULFILLMENT_DATA_',
        '_POST_FULFILLMENT_ORDER_REQUEST_DATA_', '_POST_FULFILLMENT_ORDER_CANCELLATION', '_POST_PAYMENT_ADJUSTMENT_DATA_', '_POST_INVOICE_CONFIRMATION_DATA_',
        '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_ORDER_ACKNOWLEDGEMENT_DATA_', '_POST_FLAT_FILE_FULFILLMENT_DATA_',
        '_POST_FLAT_FILE_FBA_CREATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_FBA_UPDATE_INBOUND_SHIPMENT_', '_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_',
        '_POST_FLAT_FILE_INVOICE_CONFIRMATION_DATA_', '_POST_FLAT_FILE_INVLOADER_DATA_', '_POST_FLAT_FILE_CONVERGENCE_LISTINGS_DATA_',
        '_POST_FLAT_FILE_BOOKLOADER_DATA_', '_POST_FLAT_FILE_LISTINGS_DATA_', '_POST_FLAT_FILE_PRICEANDQUANTITYONLY', '_POST_UIEE_BOOKLOADER_DATA_'
    ],

    /**
     * @abstract Enumerates all the feed processing status values that are available through the Feeds API section.
     * @description The FeedProcessingStatus enumeration describes the processing status of a submitted feed.
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_FeedProcessingStatus.html
     */
    FeedProcessingStatus: [
        '_AWAITING_ASYNCHRONOUS_REPLY_', '_CANCELLED_', '_DONE_', '_IN_PROGRESS_', '_IN_SAFETY_NET_', '_SUBMITTED_', '_UNCONFIRMED_'
    ],

    /**
     * @abstract Cancels one or more feed submissions and returns a count of the feed submissions that were canceled.
     * @description The CancelFeedSubmissions operation cancels one or more feed submissions and returns a count of the canceled feed submissions and the feed submission information. Note that if you do not specify a FeedSubmmissionId, all feed submissions are canceled.

     Information is returned for the first 100 feed submissions in the list. To return information for more than 100 canceled feed submissions, use the GetFeedSubmissionList operation.

     If a feed has begun processing, it cannot be canceled.

     The CancelFeedSubmissions operation has a maximum request quota of 10 and a restore rate of one request every 45 seconds. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional: FeedSubmissionIdList, FeedTypeList, SubmittedFromDAte, SubmittedToDate
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_CancelFeedSubmissions.html
     */
    CancelFeedSubmissions: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }

        var parameters = {};
        if (_.has(params, 'FeedTypeList')) {
            for (var key in params['FeedTypeList']) {
                if (/FeedTypeList.Type.[0-9]+/.test(key) && _.contains(this.FeedTypes, params['FeedTypeList'][key])) {
                    parameters[key] = params['FeedTypeList'][key];
                }
            }
        }

        if (_.has(params, 'FeedSubmissionIdList')) {
            for (var key in params['FeedSubmissionIdList']) {
                if (/FeedSubmissionIdList.Id.[0-9]+/.test(key)) {
                    parameters[key] = params['FeedSubmissionIdList'][key];
                }
            }
        }

        if (_.has(params, 'SubmittedFromDate')) {
            parameters['SubmittedFromDate'] = params['SubmittedFromDate'];
        }

        if (_.has(params, 'SubmittedToDate')) {
            parameters['SubmittedToDate'] = params['SubmittedToDate'];
        }

        mws.createRequest({
            'action': 'CancelFeedSubmissions',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });
    },

    /**
     * @abstract Returns a list of all feed submissions submitted in the previous 90 days.
     * @description

     The GetFeedSubmissionList operation returns a list of feed submissions submitted in the previous 90 days that match the query parameters. Use this operation to determine the status of a feed submission by passing in the FeedProcessingId that was returned by the SubmitFeed operation.

     The GetFeedSubmissionList request can return a maximum of 100 results. If there are additional results to return, HasNext is returned in the response with a true value. To retrieve all the results, you can pass the value of the NextToken parameter to the GetFeedSubmissionListByNextToken operation and repeat until HasNext is false.

     The GetFeedSubmissionList operation has a maximum request quota of 10 and a restore rate of one request every 45 seconds. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional: FeedSubmissionIdList,MaxCount, FeedTypeList, FeedProcessingStatusList, SubmittedFromDate, SubmittedToDate
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_GetFeedSubmissionList.html
     */
    GetFeedSubmissionList: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }
        var parameters = {};
        if (_.has(params, 'FeedSubmissionIdList')) {
            if (!_.isEmpty(params['FeedSubmissionIdList'])) {
                for (var key in params['FeedSubmissionIdList']) {
                    if (/FeedSubmissionIdLis.Id.[1-9]+/.test(key)) {
                        parameters[key] = params['FeedSubmissionIdList'][key];
                    }
                }
            }
        }

        if (_.has(params, 'MaxCount')) {
            parameters['MaxCount'] = params['MaxCount'];
        }

        if (_.has(params, 'FeedTypeList')) {
            for (var key in params['FeedTypeList']) {
                if (/FeedTypeList.Type.[0-9]+/.test(key) && _.contains(this.FeedTypes, params['FeedTypeList'][key])) {
                    parameters[key] = params['FeedTypeList'][key];
                }
            }
        }

        if (_.has(params, 'FeedProcessingStatusList')) {
            for (var key in params['FeedProcessingStatusList']) {
                if (/FeedProcessingStatusList.Status.[0-9]+/.test(key) && _.has(this.FeedProcessingStatus, params['FeedProcessingStatusList'][key])) {
                    parameters[key] = params['FeedProcessingStatusList'][key];
                }
            }
        }

        if (_.has(params, 'SubmittedFromDate')) {
            parameters['SubmittedFromDate'] = params['SubmittedFromDate'];
        }

        if (_.has(params, 'SubmittedToDate')) {
            parameters['SubmittedToDate'] = params['SubmittedToDate'];
        }

        mws.createRequest({
            'action': 'GetFeedSubmissionList',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });
    },

    /**
     * @abstract Returns a list of feed submissions using the NextToken parameter.
     * @description The GetFeedSubmissionListByNextToken operation returns a list of feed submissions that match the query parameters. It uses the NextToken, which was supplied in a previous request to either the GetFeedSubmissionListByNextToken operation or the GetFeedSubmissionList operation where the value of HasNext was true.

     The GetFeedSubmissionListByNextToken operation has a maximum request quota of 30 and a restore rate of one request every two seconds. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.
     * @param {Object} config Configuration file
     * @param {Object} params Required: NextToken Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_GetFeedSubmissionListByNextToken.html
     */
    GetFeedSubmissionListByNextToken: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }
        if (!_.has(params, 'NextToken')) {
            cb({
                'Error': 'You need to pass parameter NextToken'
            }, null);
            return;
        }
        var parameters = {
            'NextToken': params['NextToken']
        };
        mws.createRequest({
            'action': 'GetFeedSubmissionListByNextToken',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });
    },

    /**
     * @abstract Returns a count of the feeds submitted in the previous 90 days.
     * @description The GetFeedSubmissionCount operation returns a count of the total number of feeds submitted in the previous 90 days.

     The GetFeedSubmissionCount operation has a maximum request quota of 10 and a restore rate of one request every 45 seconds. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     * @param {Object} config Configuration file
     * @param {Object} params Required:  Optional: FeedProcessingStatusList, FeedTypeList, SubmittedFromDAte, SubmittedToDate
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_GetFeedSubmissionCount.html
     */
    GetFeedSubmissionCount: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }

        var parameters = {};
        if (_.has(params, 'FeedTypeList')) {
            for (var key in params['FeedTypeList']) {
                if (/FeedTypeList.Type.[0-9]+/.test(key) && _.contains(this.FeedTypes, params['FeedTypeList'][key])) {
                    parameters[key] = params['FeedTypeList'][key];
                }
            }
        }

        if (_.has(params, 'FeedProcessingStatusList')) {
            for (var key in params['FeedProcessingStatusList']) {
                if (/FeedProcessingStatusList.Status.[0-9]+/.test(key) && _.has(this.FeedProcessingStatus, params['FeedProcessingStatusList'][key])) {
                    parameters[key] = params['FeedProcessingStatusList'][key];
                }
            }
        }

        if (_.has(params, 'SubmittedFromDate')) {
            parameters['SubmittedFromDate'] = params['SubmittedFromDate'];
        }

        if (_.has(params, 'SubmittedToDate')) {
            parameters['SubmittedToDate'] = params['SubmittedToDate'];
        }

        mws.createRequest({
            'action': 'GetFeedSubmissionCount',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });
    },

    /**
     * @abstract Returns the feed processing report and the Content-MD5 header.
     * @description The GetFeedSubmissionResult operation returns the feed processing report and the Content-MD5 header for the returned HTTP body.

     You should compute the MD5 hash of the HTTP body of the report that Amazon MWS returned to you, and compare that with the Content-MD5 header value that is returned. If the computed hash value and the returned hash value do not match, the report body was corrupted during transmission. You should discard the result and automatically retry the request for up to three more times. Please notify Amazon MWS if you receive a corrupted report body. For more information on the Content-MD5 header, see the Amazon MWS Developer Guide.

     The GetFeedSubmissionResult operation has a maximum request quota of 15 and a restore rate of one request per minute. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.
     * @param {Object} config Configuration file
     * @param {Object} params Required: FeedSubmissionId Optional:
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_GetFeedSubmissionResult.html
     */
    GetFeedSubmissionResult: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }

        if(!_.has(params,'FeedSubmissionId')){
            cb({
                'Error' : 'You need to pass parameter FeedSubmissionId'
            }, null);
            return;
        }
        var parameters = {
            'FeedSubmissionId' : params['FeedSubmissionId']
        };

        mws.createRequest({
            'action': 'GetFeedSubmissionResult',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        })
    },

    /**
     * @abstract Uploads a feed for processing by Amazon MWS.
     * @description  The SubmitFeed operation uploads a file and any necessary metadata for processing. Note that you must calculate a Content-MD5 header for the submitted file. For more information about creating a Content-MD5 header, see What you should know about the Amazon MWS Feeds API section.

     The SubmitFeed operation has a maximum request quota of 15 and a restore rate of one request every two minutes. For definitions of throttling terminology and for a complete explanation of throttling, see Throttling: Limits to how often you can submit requests in the Amazon MWS Developer Guide.

     Feed size is limited to 2 GB (231-1, or 2,147,483,647 bytes) per feed. If you have a large amount of data to submit, you should submit feeds smaller than the feed size limit by breaking up the data, or submit the feeds over a period of time. For optimal performance, a good practice is to submit feeds with a size limit of 30,000 records/items, or submit feeds over a period of time, such as every few hours.
     * @param {Object} config Configuration file
     * @param {Object} params Required: FeedType Optional: MarketplaceIdList, PurgeAndReplace
     * @param {Boolean} xml If true, result will be send as raw xml, although would be converted to javascript object
     * @param {Function} cb Callback function
     * @see http://docs.developer.amazonservices.com/en_US/feeds/Feeds_SubmitFeed.html
     */
    SubmitFeed: function (config, params, xml, cb) {
        if (typeof xml == 'function') {
            cb = xml;
            xml = false;
        } else if (typeof xml == 'undefined') {
            console.warn("No callback provided. Throwing the request");
            return;
        }

        if (!_.has(params, 'FeedType')) {
            cb({
                'Error': 'You need to pass parameter FeedType'
            }, null);
            return;
        }
        if (!_.contains(this.FeedTypes, params['FeedType'])) {
            cb({
                'Error': 'FeedType must be one of the FeedTypes listed in AmazonDocs'
            }, null);
            return;
        }

        parameters = {
            'FeedType': params['FeedType']
        };

        if (_.has(params, 'MarketplaceIdList')) {
            var counter = 1;
            for (var key in params['MarketplaceIdList']) {
                if (/MarketplaceIdList.Id.[1-9]+/.test(key)) {
                    parameters[key] = params['MarketplaceIdList'][key];
                }
                counter++;
                if (counter > 20) {
                    break;
                }
            }
        }
        if (_.has(params, 'PurgeAndReplace')) {
            if (typeof params['PurgeAndReplace'] === 'boolean') {
                parameters['PurgeAndReplace'] = params['PurgeAndReplace'];
            }
        }

        mws.createRequest({
            'action': 'SubmitFeed',
            'config': config,
            'parameters': parameters,
            'type': 'Feeds',
            'xml': xml
        }, function (err, data) {
            cb(err, data);
        });

    }

};
<|endoftext|>


<commit_before>/*  __  __    _    _  _______    ____ _   _    _    _   _  ____ _____ ____  _
   |  \/  |  / \  | |/ / ____|  / ___| | | |  / \  | \ | |/ ___| ____/ ___|| |
   | |\/| | / _ \ | ' /|  _|   | |   | |_| | / _ \ |  \| | |  _|  _| \___ \| |
   | |  | |/ ___ \| . \| |___  | |___|  _  |/ ___ \| |\  | |_| | |___ ___) |_|
   |_|  |_/_/   \_\_|\_\_____|  \____|_| |_/_/   \_\_| \_|\____|_____|____/(_)

   There is a 100% chance that this project can use improvements.
   Pull requests are ALWAYS welcome, even if just amounts to a conversation.  */

'use strict';

var fs = require('fs');
var fsExtra = require('fs-extra');
var path = require('path');
var ospath = require('ospath');
var request = require('request');
var uuid = require('uuid');
var gunzip = require('gunzip-maybe');
var tar = require('tar-fs');
var isDirectory = require('is-directory').sync;
var async = require('async');
var exec = require('child_process').exec;

var fetchGitHub = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    // setup the tmp path
    fs.mkdir(tmpPath, function () {
        // get the default branch
        var query = (process.env.token) ? { access_token: process.env.token } : {};

        request({
            url: 'https://api.github.com/repos/' + inputUrl,
            qs: query,
            headers: {
                'User-Agent': 'request'
            }
        }, function (err, response) {
            var repoDetails, gitRef;

            if (err) {
                callback(err);
                return;
            }

            repoDetails = JSON.parse(response.body);
            gitRef = inputRef || repoDetails.default_branch;

            request
                .get('https://github.com/' + inputUrl + '/archive/' + gitRef + '.tar.gz')
                .pipe(gunzip())
                .pipe(tar.extract(tmpPath))
                .on('finish', function () {
                    state.template.tmp = tmpPath + '/' + repoDetails.name + '-' + gitRef;
                    callback(null, state);
                });
        });
    });
};

var fetchDirectory = function (state, callback) {
    var input = state.template.input,
        tmpPath = ospath.tmp() + '/' + uuid.v1();

    state.template.tmp = tmpPath;

    // setup the tmp path
    fsExtra.mkdirs(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        fsExtra.copy(input, tmpPath, function (err) {
            if (err) {
                callback(err);
                return;
            }
            if (isDirectory(tmpPath + '/.git')) {
                fsExtra.remove(tmpPath + '/.git', function (err) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    callback(null, state);
                });
            } else {
                callback(null, state);
            }
        });
    });
};

var fetchGit = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    state.template.tmp = tmpPath;

    async.series([
        function (next) {
            exec('git clone ' + inputUrl + ' ' + tmpPath, function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(null);
            });
        },
        function (next) {
            if (inputRef) {
                exec('cd ' + tmpPath + ' && git checkout ' + inputRef, function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        },
        function (next) {
            if (isDirectory(tmpPath + '/.git')) {
                fsExtra.remove(tmpPath + '/.git', function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        }
    ], function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback(null, state);
    });
};

var fetchURL = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input,
        response = request(input);

    state.project.tmp = tmpPath;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        response
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchJSON = function (state, callback) {
    callback(null, state);
};

var fetchFile = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        fs.createReadStream(input)
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchTemplate = function (state, callback) {
    switch (state.template.source) {
    case 'github':
        fetchGitHub(state, callback);
        break;
    case 'directory':
        fetchDirectory(state, callback);
        break;
    case 'git':
        fetchGit(state, callback);
        break;
    }
};

var fetchProject = function (state, callback) {
    switch (state.project.source) {
    case 'url':
        fetchURL(state, callback);
        break;
    case 'json':
        fetchJSON(state, callback);
        break;
    case 'file':
        fetchFile(state, callback);
        break;
    }
};

module.exports = function (state, callback) {
    fetchTemplate(state, function (err, state) {
        if (err) {
            callback(err);
            return;
        }
        if (state.project !== undefined) {
            fetchProject(state, function (err, state) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, state);
            });
        } else {
            callback(null, state);
        }
    });
};

<commit_msg>refactor(fetchGit): use `cwd` option instead of `cd`<commit_after>/*  __  __    _    _  _______    ____ _   _    _    _   _  ____ _____ ____  _
   |  \/  |  / \  | |/ / ____|  / ___| | | |  / \  | \ | |/ ___| ____/ ___|| |
   | |\/| | / _ \ | ' /|  _|   | |   | |_| | / _ \ |  \| | |  _|  _| \___ \| |
   | |  | |/ ___ \| . \| |___  | |___|  _  |/ ___ \| |\  | |_| | |___ ___) |_|
   |_|  |_/_/   \_\_|\_\_____|  \____|_| |_/_/   \_\_| \_|\____|_____|____/(_)

   There is a 100% chance that this project can use improvements.
   Pull requests are ALWAYS welcome, even if just amounts to a conversation.  */

'use strict';

var fs = require('fs');
var fsExtra = require('fs-extra');
var path = require('path');
var ospath = require('ospath');
var request = require('request');
var uuid = require('uuid');
var gunzip = require('gunzip-maybe');
var tar = require('tar-fs');
var isDirectory = require('is-directory').sync;
var async = require('async');
var exec = require('child_process').exec;

var fetchGitHub = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    // setup the tmp path
    fs.mkdir(tmpPath, function () {
        // get the default branch
        var query = (process.env.token) ? { access_token: process.env.token } : {};

        request({
            url: 'https://api.github.com/repos/' + inputUrl,
            qs: query,
            headers: {
                'User-Agent': 'request'
            }
        }, function (err, response) {
            var repoDetails, gitRef;

            if (err) {
                callback(err);
                return;
            }

            repoDetails = JSON.parse(response.body);
            gitRef = inputRef || repoDetails.default_branch;

            request
                .get('https://github.com/' + inputUrl + '/archive/' + gitRef + '.tar.gz')
                .pipe(gunzip())
                .pipe(tar.extract(tmpPath))
                .on('finish', function () {
                    state.template.tmp = tmpPath + '/' + repoDetails.name + '-' + gitRef;
                    callback(null, state);
                });
        });
    });
};

var fetchDirectory = function (state, callback) {
    var input = state.template.input,
        tmpPath = ospath.tmp() + '/' + uuid.v1();

    state.template.tmp = tmpPath;

    // setup the tmp path
    fsExtra.mkdirs(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        fsExtra.copy(input, tmpPath, function (err) {
            if (err) {
                callback(err);
                return;
            }
            if (isDirectory(tmpPath + '/.git')) {
                fsExtra.remove(tmpPath + '/.git', function (err) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    callback(null, state);
                });
            } else {
                callback(null, state);
            }
        });
    });
};

var fetchGit = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    state.template.tmp = tmpPath;

    async.series([
        function (next) {
            exec('git clone ' + inputUrl + ' ' + tmpPath, function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(null);
            });
        },
        function (next) {
            if (inputRef) {
                exec('git checkout ' + inputRef, {
                    cwd: tmpPath
                }, function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        },
        function (next) {
            if (isDirectory(tmpPath + '/.git')) {
                fsExtra.remove(tmpPath + '/.git', function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        }
    ], function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback(null, state);
    });
};

var fetchURL = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input,
        response = request(input);

    state.project.tmp = tmpPath;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        response
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchJSON = function (state, callback) {
    callback(null, state);
};

var fetchFile = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        fs.createReadStream(input)
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchTemplate = function (state, callback) {
    switch (state.template.source) {
    case 'github':
        fetchGitHub(state, callback);
        break;
    case 'directory':
        fetchDirectory(state, callback);
        break;
    case 'git':
        fetchGit(state, callback);
        break;
    }
};

var fetchProject = function (state, callback) {
    switch (state.project.source) {
    case 'url':
        fetchURL(state, callback);
        break;
    case 'json':
        fetchJSON(state, callback);
        break;
    case 'file':
        fetchFile(state, callback);
        break;
    }
};

module.exports = function (state, callback) {
    fetchTemplate(state, function (err, state) {
        if (err) {
            callback(err);
            return;
        }
        if (state.project !== undefined) {
            fetchProject(state, function (err, state) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, state);
            });
        } else {
            callback(null, state);
        }
    });
};

<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter
	, fs = require('fs')
	, _ = require('underscore')
	, winston = require('./logging').winston
	, util = require('util')
	, watch = require('watch')
	, uuid = require("node-uuid")
;

var bundlesFolder = GLOBAL.config.isLocal ? process.cwd() + '/bundles' : process.cwd();
if (GLOBAL.config.isLocal && !fs.existsSync(bundlesFolder)) fs.mkdirSync(bundlesFolder);

if (!String.prototype.endsWith) {
    Object.defineProperty(String.prototype, 'endsWith', {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function (searchString, position) {
            position = position || this.length;
            position = position - searchString.length;
            return this.lastIndexOf(searchString) === position;
        }
    });
}

var addSampleBundle = function() {
	var sampleFile = bundlesFolder + '/sample.js';
	var sample = "var spashttp = require(\"spas-http\");\n\nexports.sample = {\n\t\"searchTweets\": {\n\t\t\"resource\": spashttp.request,\n\t\t\"params\": {\n\t\t\t\"url\": \"http://search.twitter.com/search.json\",\n\t\t\t\"q\": \"dtex\",\n\t\t\t\"lang\": \"en\",\n\t\t\t\"count\": 100 \n\t\t},\n\t\t\"cacheduration\": 3600,\n\t\t\"timeout\": 500,\n\t\t\"filter\": {\n\t\t\t\"results\": [{\n\t\t\t\t\"from_user\": true,\n\t\t\t\t\"text\": true }]\n\t\t}\n\t},\n\t\"searchMoreTweets\": {\n\t\t\"resource\": spashttp.request,\n\t\t\"params\": {\n\t\t\t\"url\": \"http://search.twitter.com/search.json\",\n\t\t\t\"q\":\"spas\",\n\t\t\t\"lang\": \"en\" \n\t\t},\n\t\t\"cacheduration\": 3600\n\t}\n}";
	fs.writeFileSync(sampleFile, sample);
}

var BundleManager = function() {

    var self = this;

    this.refreshBundles = function( targetFile, callback ) {

		winston.info('bundleManager refreshBundles');

		var bundles = {}
			, files = fs.readdirSync(bundlesFolder);

		_.each(files, function(file, idx) {
			if (file.indexOf('.js') !== -1 && file.indexOf('config.json') === -1 && (file[0] !== '.' || (targetFile && targetFile.indexOf(file) != -1 ))) {
				try {
					var tempBundle =  require(bundlesFolder + "/" + file);;
					_.each(tempBundle, function(part, key) {
						winston.info("Bundle \""+key+"\": loaded from \"" + file + "\"");
						bundles[key] = part;
					});
				} catch(err) {
					winston.error("Error parsing bundle \""+file+"\": "+err);
				}
			}

		});

		GLOBAL.bundles = bundles;

		// ### Stop all the existing scheduled jobs
    	_.each(GLOBAL.cronjobs, function (job, idx) {
	    	job.stop();
	    	delete job;
    	});

    	GLOBAL.cronjobs = [];

    	// ### Schedule jobs defined in bundles
		_.each(bundles, function (bundle, bid) {
			_.each(bundle, function (api, key) {
				if (api.schedule) {
					var job = new cronJob(api.schedule, function(){
			    		winston.info('cronjob '+key+' called');
			    		engine.refresh(api, key, bid, bundle);
			    	}, null, true);
			    	GLOBAL.cronjobs.push(job);
			    }
			});
		});

		if (callback) callback();

	}

	// Watch the bundles directory for changes
	watch.createMonitor(bundlesFolder, { "ignoreDotFiles": true }, function (monitor) {

		monitor.on("created", function (file, stat) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file created: ' + file);
				self.refreshBundles();
			}
		});

		monitor.on("changed", function (file, curr, prev) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file changed: ' + file);

				// To bust node's module caching we rename the file before calling updateBundle
				var tempFile = '.'+uuid.v4();
				fs.renameSync(file, tempFile);
				self.refreshBundles(tempFile, function() {
					fs.renameSync(tempFile, file);
				});
			}
		});

		monitor.on("removed", function (file, stat) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file removed: ' + file);
				self.refreshBundles();
			}
		});

	});

};

// If --create was passed in the commandline, create sample.js and package.json
if ( GLOBAL.config.args.create ) {
	addSampleBundle();
}

util.inherits(BundleManager, EventEmitter);

module.exports = BundleManager;
<commit_msg>Added some comments and ignore package.json<commit_after>var EventEmitter = require('events').EventEmitter
	, fs = require('fs')
	, _ = require('underscore')
	, winston = require('./logging').winston
	, util = require('util')
	, watch = require('watch')
	, uuid = require("node-uuid")
;

// ### The bundles folder location depends on wether we are running spas globally or not
var bundlesFolder = GLOBAL.config.isLocal ? process.cwd() + '/bundles' : process.cwd();

// ### If the bundles folder is needed but doesn't exist, create it
if (GLOBAL.config.isLocal && !fs.existsSync(bundlesFolder)) fs.mkdirSync(bundlesFolder);

// ### adds endsWith if it is not present on String object
if (!String.prototype.endsWith) {
    Object.defineProperty(String.prototype, 'endsWith', {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function (searchString, position) {
            position = position || this.length;
            position = position - searchString.length;
            return this.lastIndexOf(searchString) === position;
        }
    });
}

// ### Add a sample bundle
var addSampleBundle = function() {
	var sampleFile = bundlesFolder + '/sample.js';
	var sample = "var spashttp = require(\"spas-http\");\n\nexports.sample = {\n\t\"searchTweets\": {\n\t\t\"resource\": spashttp.request,\n\t\t\"params\": {\n\t\t\t\"url\": \"http://search.twitter.com/search.json\",\n\t\t\t\"q\": \"dtex\",\n\t\t\t\"lang\": \"en\",\n\t\t\t\"count\": 100 \n\t\t},\n\t\t\"cacheduration\": 3600,\n\t\t\"timeout\": 500,\n\t\t\"filter\": {\n\t\t\t\"results\": [{\n\t\t\t\t\"from_user\": true,\n\t\t\t\t\"text\": true }]\n\t\t}\n\t},\n\t\"searchMoreTweets\": {\n\t\t\"resource\": spashttp.request,\n\t\t\"params\": {\n\t\t\t\"url\": \"http://search.twitter.com/search.json\",\n\t\t\t\"q\":\"spas\",\n\t\t\t\"lang\": \"en\" \n\t\t},\n\t\t\"cacheduration\": 3600\n\t}\n}";
	fs.writeFileSync(sampleFile, sample);
}

// ## The BundleManager is responsible for loading and monitoring the bundles directory for changes
var BundleManager = function() {

    var self = this;

    // Either this is the first load or a bundle has been added, deleted or changed
    this.refreshBundles = function( targetFile, callback ) {

		winston.info('bundleManager refreshBundles');

		var bundles = {}
			, files = fs.readdirSync(bundlesFolder);

		_.each(files, function(file, idx) {

			// Make sure we ignore non-js files, dotfiles, packae.json and config.json
			if (file.indexOf('.js') !== -1 && file.indexOf('package.json') === -1 && file.indexOf('config.json') === -1 && (file[0] !== '.' || (targetFile && targetFile.indexOf(file) != -1 ))) {
				// if any part fo the bundle is not valid js an error will be thrown and we ignore the rest of the file
				try {
					var tempBundle =  require(bundlesFolder + "/" + file);
					_.each(tempBundle, function(part, key) {
						winston.info("Bundle \""+key+"\": loaded from \"" + file + "\"");
						bundles[key] = part;
					});
				} catch(err) {
					winston.error("Error parsing bundle \""+file+"\": "+err);
				}
			}

		});

		GLOBAL.bundles = bundles;

		// ### Stop all the existing scheduled jobs
    	_.each(GLOBAL.cronjobs, function (job, idx) {
	    	job.stop();
	    	delete job;
    	});
		GLOBAL.cronjobs = [];

    	// ### Schedule jobs defined in bundles
		_.each(bundles, function (bundle, bid) {
			_.each(bundle, function (api, key) {
				if (api.schedule) {
					var job = new cronJob(api.schedule, function(){
			    		winston.info('cronjob '+key+' called');
			    		engine.refresh(api, key, bid, bundle);
			    	}, null, true);
			    	GLOBAL.cronjobs.push(job);
			    }
			});
		});

		if (callback) callback();

	}

	// Watch the bundles directory for changes
	watch.createMonitor(bundlesFolder, { "ignoreDotFiles": true }, function (monitor) {

		monitor.on("created", function (file, stat) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file created: ' + file);
				self.refreshBundles();
			}
		});

		monitor.on("changed", function (file, curr, prev) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file changed: ' + file);

				// To bust node's module caching we rename the file before calling updateBundle
				var tempFile = '.'+uuid.v4();
				fs.renameSync(file, tempFile);
				self.refreshBundles(tempFile, function() {
					// Now we can change the bundle name back to its original name
					fs.renameSync(tempFile, file);
				});
			}
		});

		monitor.on("removed", function (file, stat) {
			if ( file.endsWith('.js') ) {
				winston.event('Bundle file removed: ' + file);
				self.refreshBundles();
			}
		});

	});

};

// If --create was passed from the commandline create sample.js bundle
if ( GLOBAL.config.args.create ) {
	addSampleBundle();
}

util.inherits(BundleManager, EventEmitter);

module.exports = BundleManager;
<|endoftext|>


<commit_before>'use strict';

var request = require('request');
var Promise = require('bluebird');

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

function contentType(url) {
  return new Promise(function (resolve, reject) {
    request.head(url, function (err, res) {
      if (err) {
        return reject(err);
      }

      if (res.statusCode !== 200) {
        return reject(new Error(
          'Resource ' + url +
          ' is not available (HTTP ' + res.statusCode + ')'
        ));
      }

      resolve((res.headers['content-type'] || 'text/plain').toLowerCase());
    });
  });
}

module.exports = function (url) {
  return contentType(url)
    .then(function (type) {
      return extract(type, request.get(url));
    });
};
<commit_msg>promisify 'request' instead of 'manually' instantiating Promise<commit_after>'use strict';

var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));

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

function contentType(url) {
  return request.headAsync(url)
    .spread(function (res) {
      if (res.statusCode !== 200) {
        throw new Error([
          'Resource ', url, ' is not available (HTTP ', res.statusCode, ')'
        ].join(''));
      }

      return (res.headers['content-type'] || 'text/plain').toLowerCase();
    });
}

module.exports = function (url) {
  return contentType(url)
    .then(function (type) {
      return extract(type, request.get(url));
    });
};
<|endoftext|>


<commit_before>/*  __  __    _    _  _______    ____ _   _    _    _   _  ____ _____ ____  _
   |  \/  |  / \  | |/ / ____|  / ___| | | |  / \  | \ | |/ ___| ____/ ___|| |
   | |\/| | / _ \ | ' /|  _|   | |   | |_| | / _ \ |  \| | |  _|  _| \___ \| |
   | |  | |/ ___ \| . \| |___  | |___|  _  |/ ___ \| |\  | |_| | |___ ___) |_|
   |_|  |_/_/   \_\_|\_\_____|  \____|_| |_/_/   \_\_| \_|\____|_____|____/(_)

   There is a 100% chance that this project can use improvements.
   Pull requests are ALWAYS welcome, even if just amounts to a conversation.  */

'use strict';

var fs = require('fs');
var mkdirp = require('mkdirp');
var cpr = require('cpr');
var rimraf = require('rimraf');
var path = require('path');
var ospath = require('ospath');
var request = require('request');
var uuid = require('uuid');
var gunzip = require('gunzip-maybe');
var tar = require('tar-fs');
var isDirectory = require('is-directory').sync;
var async = require('async');
var exec = require('child_process').exec;

var fetchGitHub = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    // setup the tmp path
    fs.mkdir(tmpPath, function () {
        // get the default branch
        var query = (process.env.token) ? { access_token: process.env.token } : {};

        request({
            url: 'https://api.github.com/repos/' + inputUrl,
            qs: query,
            headers: {
                'User-Agent': 'request'
            }
        }, function (err, response) {
            var repoDetails, gitRef;

            if (err) {
                callback(err);
                return;
            }

            repoDetails = JSON.parse(response.body);
            gitRef = inputRef || repoDetails.default_branch;

            request
                .get('https://github.com/' + inputUrl + '/archive/' + gitRef + '.tar.gz')
                .pipe(gunzip())
                .pipe(tar.extract(tmpPath))
                .on('finish', function () {
                    state.template.tmp = tmpPath + '/' + repoDetails.name + '-' + gitRef;
                    callback(null, state);
                });
        });
    });
};

var fetchDirectory = function (state, callback) {
    var input = state.template.input,
        tmpPath = ospath.tmp() + '/' + uuid.v1();

    state.template.tmp = tmpPath;

    // setup the tmp path
    mkdirp(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        cpr(input, tmpPath, function (err) {
            if (err) {
                callback(err);
                return;
            }
            if (isDirectory(tmpPath + '/.git')) {
                rimraf(tmpPath + '/.git', function (err) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    callback(null, state);
                });
            } else {
                callback(null, state);
            }
        });
    });
};

var fetchGit = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    state.template.tmp = tmpPath;

    async.series([
        function (next) {
            exec('git clone ' + inputUrl + ' ' + tmpPath, function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(null);
            });
        },
        function (next) {
            if (inputRef) {
                exec('git checkout ' + inputRef, {
                    cwd: tmpPath
                }, function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        }
    ], function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback(null, state);
    });
};

var fetchURL = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input,
        response = request(input);

    state.project.tmp = tmpPath;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        response
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchJSON = function (state, callback) {
    callback(null, state);
};

var fetchFile = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        fs.createReadStream(input)
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchTemplate = function (state, callback) {
    switch (state.template.source) {
    case 'github':
        fetchGitHub(state, callback);
        break;
    case 'directory':
        fetchDirectory(state, callback);
        break;
    case 'git':
        fetchGit(state, callback);
        break;
    }
};

var fetchProject = function (state, callback) {
    switch (state.project.source) {
    case 'url':
        fetchURL(state, callback);
        break;
    case 'json':
        fetchJSON(state, callback);
        break;
    case 'file':
        fetchFile(state, callback);
        break;
    }
};

module.exports = function (state, callback) {
    fetchTemplate(state, function (err, state) {
        if (err) {
            callback(err);
            return;
        }
        if (state.project !== undefined) {
            fetchProject(state, function (err, state) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, state);
            });
        } else {
            callback(null, state);
        }
    });
};

<commit_msg>Updating fetch to utilize git binaries for GitHub vs API<commit_after>/*  __  __    _    _  _______    ____ _   _    _    _   _  ____ _____ ____  _
   |  \/  |  / \  | |/ / ____|  / ___| | | |  / \  | \ | |/ ___| ____/ ___|| |
   | |\/| | / _ \ | ' /|  _|   | |   | |_| | / _ \ |  \| | |  _|  _| \___ \| |
   | |  | |/ ___ \| . \| |___  | |___|  _  |/ ___ \| |\  | |_| | |___ ___) |_|
   |_|  |_/_/   \_\_|\_\_____|  \____|_| |_/_/   \_\_| \_|\____|_____|____/(_)

   There is a 100% chance that this project can use improvements.
   Pull requests are ALWAYS welcome, even if just amounts to a conversation.  */

'use strict';

var fs = require('fs');
var mkdirp = require('mkdirp');
var cpr = require('cpr');
var rimraf = require('rimraf');
var path = require('path');
var ospath = require('ospath');
var request = require('request');
var uuid = require('uuid');
var gunzip = require('gunzip-maybe');
var tar = require('tar-fs');
var isDirectory = require('is-directory').sync;
var async = require('async');
var exec = require('child_process').exec;

var fetchGitHub = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1],
        url = 'https://github.com/' + inputUrl + '.git';

    state.template.tmp = tmpPath;

    gitClone(url, inputRef, tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback(null, state);
    });
};

var fetchDirectory = function (state, callback) {
    var input = state.template.input,
        tmpPath = ospath.tmp() + '/' + uuid.v1();

    state.template.tmp = tmpPath;

    // setup the tmp path
    mkdirp(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        cpr(input, tmpPath, function (err) {
            if (err) {
                callback(err);
                return;
            }
            if (isDirectory(tmpPath + '/.git')) {
                rimraf(tmpPath + '/.git', function (err) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    callback(null, state);
                });
            } else {
                callback(null, state);
            }
        });
    });
};

var fetchGit = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        inputUrl = state.template.input.split('#')[0],
        inputRef = state.template.input.split('#')[1];

    state.template.tmp = tmpPath;

    gitClone(inputUrl, inputRef, tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback(null, state);
    });
};

var fetchURL = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input,
        response = request(input);

    state.project.tmp = tmpPath;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        response
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchJSON = function (state, callback) {
    callback(null, state);
};

var fetchFile = function (state, callback) {
    var tmpPath = ospath.tmp() + '/' + uuid.v1(),
        input = state.project.input;

    // setup the tmp path
    fs.mkdir(tmpPath, function (err) {
        if (err) {
            callback(err);
            return;
        }

        state.project.tmp = tmpPath + '/data.json';

        fs.createReadStream(input)
            .pipe(fs.createWriteStream(tmpPath + '/data.json'))
            .on('finish', function () {
                callback(null, state);
            });
    });
};

var fetchTemplate = function (state, callback) {
    switch (state.template.source) {
    case 'github':
        fetchGitHub(state, callback);
        break;
    case 'directory':
        fetchDirectory(state, callback);
        break;
    case 'git':
        fetchGit(state, callback);
        break;
    }
};

var fetchProject = function (state, callback) {
    switch (state.project.source) {
    case 'url':
        fetchURL(state, callback);
        break;
    case 'json':
        fetchJSON(state, callback);
        break;
    case 'file':
        fetchFile(state, callback);
        break;
    }
};

var gitClone = function (url, ref, tmpPath, callback) {
    async.series([
        function (next) {
            exec('git clone ' + url + ' ' + tmpPath, function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(null);
            });
        },
        function (next) {
            if (ref) {
                exec('git checkout ' + ref, {
                    cwd: tmpPath
                }, function (err) {
                    if (err) {
                        next(err);
                        return;
                    }
                    next(null);
                });
            } else {
                next(null);
            }
        }
    ], function (err) {
        if (err) {
            callback(err);
            return;
        }
        callback();
    });
};

module.exports = function (state, callback) {
    fetchTemplate(state, function (err, state) {
        if (err) {
            callback(err);
            return;
        }
        if (state.project !== undefined) {
            fetchProject(state, function (err, state) {
                if (err) {
                    callback(err);
                    return;
                }
                callback(null, state);
            });
        } else {
            callback(null, state);
        }
    });
};

<|endoftext|>