http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/api/attributes.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/api/attributes.js 
b/node_modules/cheerio/lib/api/attributes.js
new file mode 100644
index 0000000..a4947bd
--- /dev/null
+++ b/node_modules/cheerio/lib/api/attributes.js
@@ -0,0 +1,482 @@
+var _ = require('lodash'),
+  $ = require('../static'),
+  utils = require('../utils'),
+  isTag = utils.isTag,
+  domEach = utils.domEach,
+  hasOwn = Object.prototype.hasOwnProperty,
+  camelCase = utils.camelCase,
+  cssCase = utils.cssCase,
+  rspace = /\s+/,
+  dataAttrPrefix = 'data-',
+
+  // Lookup table for coercing string data-* attributes to their corresponding
+  // JavaScript primitives
+  primitives = {
+    null: null,
+    true: true,
+    false: false
+  },
+
+  // Attributes that are booleans
+  rboolean = 
/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+  // Matches strings that look like JSON objects or arrays
+  rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
+
+
+var getAttr = function(elem, name) {
+  if (!elem || !isTag(elem)) return;
+
+  if (!elem.attribs) {
+    elem.attribs = {};
+  }
+
+  // Return the entire attribs object if no attribute specified
+  if (!name) {
+    return elem.attribs;
+  }
+
+  if (hasOwn.call(elem.attribs, name)) {
+    // Get the (decoded) attribute
+    return rboolean.test(name) ? name : elem.attribs[name];
+  }
+
+  // Mimic the DOM and return text content as value for `option's`
+  if (elem.name === 'option' && name === 'value') {
+    return $.text(elem.children);
+  }
+};
+
+var setAttr = function(el, name, value) {
+
+  if (value === null) {
+    removeAttribute(el, name);
+  } else {
+    el.attribs[name] = value+'';
+  }
+};
+
+exports.attr = function(name, value) {
+  // Set the value (with attr map support)
+  if (typeof name === 'object' || value !== undefined) {
+    if (typeof value === 'function') {
+      return domEach(this, function(i, el) {
+        setAttr(el, name, value.call(el, i, el.attribs[name]));
+      });
+    }
+    return domEach(this, function(i, el) {
+      if (!isTag(el)) return;
+
+      if (typeof name === 'object') {
+        _.each(name, function(value, name) {
+          setAttr(el, name, value);
+        });
+      } else {
+        setAttr(el, name, value);
+      }
+    });
+  }
+
+  return getAttr(this[0], name);
+};
+
+var getProp = function (el, name) {
+  return el.hasOwnProperty(name)
+      ? el[name]
+      : rboolean.test(name)
+          ? getAttr(el, name) !== undefined
+          : getAttr(el, name);
+};
+
+var setProp = function (el, name, value) {
+  el[name] = rboolean.test(name) ? !!value : value;
+};
+
+exports.prop = function (name, value) {
+  var i = 0,
+      property;
+
+  if (typeof name === 'string' && value === undefined) {
+
+    switch (name) {
+      case 'style':
+        property = this.css();
+
+        _.each(property, function (v, p) {
+          property[i++] = p;
+        });
+
+        property.length = i;
+
+        break;
+      case 'tagName':
+      case 'nodeName':
+        property = this[0].name.toUpperCase();
+        break;
+      default:
+        property = getProp(this[0], name);
+    }
+
+    return property;
+  }
+
+  if (typeof name === 'object' || value !== undefined) {
+
+    if (typeof value === 'function') {
+      return domEach(this, function(i, el) {
+        setProp(el, name, value.call(el, i, getProp(el, name)));
+      });
+    }
+
+    return domEach(this, function(i, el) {
+      if (!isTag(el)) return;
+
+      if (typeof name === 'object') {
+
+        _.each(name, function(val, name) {
+          setProp(el, name, val);
+        });
+
+      } else {
+        setProp(el, name, value);
+      }
+    });
+
+  }
+};
+
+var setData = function(el, name, value) {
+  if (!el.data) {
+    el.data = {};
+  }
+
+  if (typeof name === 'object') return _.extend(el.data, name);
+  if (typeof name === 'string' && value !== undefined) {
+    el.data[name] = value;
+  } else if (typeof name === 'object') {
+    _.exend(el.data, name);
+  }
+};
+
+// Read the specified attribute from the equivalent HTML5 `data-*` attribute,
+// and (if present) cache the value in the node's internal data store. If no
+// attribute name is specified, read *all* HTML5 `data-*` attributes in this
+// manner.
+var readData = function(el, name) {
+  var readAll = arguments.length === 1;
+  var domNames, domName, jsNames, jsName, value, idx, length;
+
+  if (readAll) {
+    domNames = Object.keys(el.attribs).filter(function(attrName) {
+      return attrName.slice(0, dataAttrPrefix.length) === dataAttrPrefix;
+    });
+    jsNames = domNames.map(function(domName) {
+      return camelCase(domName.slice(dataAttrPrefix.length));
+    });
+  } else {
+    domNames = [dataAttrPrefix + cssCase(name)];
+    jsNames = [name];
+  }
+
+  for (idx = 0, length = domNames.length; idx < length; ++idx) {
+    domName = domNames[idx];
+    jsName = jsNames[idx];
+    if (hasOwn.call(el.attribs, domName)) {
+      value = el.attribs[domName];
+
+      if (hasOwn.call(primitives, value)) {
+        value = primitives[value];
+      } else if (value === String(Number(value))) {
+        value = Number(value);
+      } else if (rbrace.test(value)) {
+        try {
+          value = JSON.parse(value);
+        } catch(e){ }
+      }
+
+      el.data[jsName] = value;
+    }
+  }
+
+  return readAll ? el.data : value;
+};
+
+exports.data = function(name, value) {
+  var elem = this[0];
+
+  if (!elem || !isTag(elem)) return;
+
+  if (!elem.data) {
+    elem.data = {};
+  }
+
+  // Return the entire data object if no data specified
+  if (!name) {
+    return readData(elem);
+  }
+
+  // Set the value (with attr map support)
+  if (typeof name === 'object' || value !== undefined) {
+    domEach(this, function(i, el) {
+      setData(el, name, value);
+    });
+    return this;
+  } else if (hasOwn.call(elem.data, name)) {
+    return elem.data[name];
+  }
+
+  return readData(elem, name);
+};
+
+/**
+ * Get the value of an element
+ */
+
+exports.val = function(value) {
+  var querying = arguments.length === 0,
+      element = this[0];
+
+  if(!element) return;
+
+  switch (element.name) {
+    case 'textarea':
+      return this.text(value);
+    case 'input':
+      switch (this.attr('type')) {
+        case 'radio':
+          if (querying) {
+            return this.attr('value');
+          } else {
+            this.attr('value', value);
+            return this;
+          }
+          break;
+        default:
+          return this.attr('value', value);
+      }
+      return;
+    case 'select':
+      var option = this.find('option:selected'),
+          returnValue;
+      if (option === undefined) return undefined;
+      if (!querying) {
+        if (!this.attr().hasOwnProperty('multiple') && typeof value == 
'object') {
+          return this;
+        }
+        if (typeof value != 'object') {
+          value = [value];
+        }
+        this.find('option').removeAttr('selected');
+        for (var i = 0; i < value.length; i++) {
+          this.find('option[value="' + value[i] + '"]').attr('selected', '');
+        }
+        return this;
+      }
+      returnValue = option.attr('value');
+      if (this.attr().hasOwnProperty('multiple')) {
+        returnValue = [];
+        domEach(option, function(i, el) {
+          returnValue.push(getAttr(el, 'value'));
+        });
+      }
+      return returnValue;
+    case 'option':
+      if (!querying) {
+        this.attr('value', value);
+        return this;
+      }
+      return this.attr('value');
+  }
+};
+
+/**
+ * Remove an attribute
+ */
+
+var removeAttribute = function(elem, name) {
+  if (!elem.attribs || !hasOwn.call(elem.attribs, name))
+    return;
+
+  delete elem.attribs[name];
+};
+
+
+exports.removeAttr = function(name) {
+  domEach(this, function(i, elem) {
+    removeAttribute(elem, name);
+  });
+
+  return this;
+};
+
+exports.hasClass = function(className) {
+  return _.some(this, function(elem) {
+    var attrs = elem.attribs,
+        clazz = attrs && attrs['class'],
+        idx = -1,
+        end;
+
+    if (clazz) {
+      while ((idx = clazz.indexOf(className, idx+1)) > -1) {
+        end = idx + className.length;
+
+        if ((idx === 0 || rspace.test(clazz[idx-1]))
+            && (end === clazz.length || rspace.test(clazz[end]))) {
+          return true;
+        }
+      }
+    }
+  });
+};
+
+exports.addClass = function(value) {
+  // Support functions
+  if (typeof value === 'function') {
+    return domEach(this, function(i, el) {
+      var className = el.attribs['class'] || '';
+      exports.addClass.call([el], value.call(el, i, className));
+    });
+  }
+
+  // Return if no value or not a string or function
+  if (!value || typeof value !== 'string') return this;
+
+  var classNames = value.split(rspace),
+      numElements = this.length;
+
+
+  for (var i = 0; i < numElements; i++) {
+    // If selected element isn't a tag, move on
+    if (!isTag(this[i])) continue;
+
+    // If we don't already have classes
+    var className = getAttr(this[i], 'class'),
+        numClasses,
+        setClass;
+
+    if (!className) {
+      setAttr(this[i], 'class', classNames.join(' ').trim());
+    } else {
+      setClass = ' ' + className + ' ';
+      numClasses = classNames.length;
+
+      // Check if class already exists
+      for (var j = 0; j < numClasses; j++) {
+        var appendClass = classNames[j] + ' ';
+        if (setClass.indexOf(' ' + appendClass) < 0)
+          setClass += appendClass;
+      }
+
+      setAttr(this[i], 'class', setClass.trim());
+    }
+  }
+
+  return this;
+};
+
+var splitClass = function(className) {
+  return className ? className.trim().split(rspace) : [];
+};
+
+exports.removeClass = function(value) {
+  var classes,
+      numClasses,
+      removeAll;
+
+  // Handle if value is a function
+  if (typeof value === 'function') {
+    return domEach(this, function(i, el) {
+      exports.removeClass.call(
+        [el], value.call(el, i, el.attribs['class'] || '')
+      );
+    });
+  }
+
+  classes = splitClass(value);
+  numClasses = classes.length;
+  removeAll = arguments.length === 0;
+
+  return domEach(this, function(i, el) {
+    if (!isTag(el)) return;
+
+    if (removeAll) {
+      // Short circuit the remove all case as this is the nice one
+      el.attribs.class = '';
+    } else {
+      var elClasses = splitClass(el.attribs.class),
+          index,
+          changed;
+
+      for (var j = 0; j < numClasses; j++) {
+        index = elClasses.indexOf(classes[j]);
+
+        if (index >= 0) {
+          elClasses.splice(index, 1);
+          changed = true;
+
+          // We have to do another pass to ensure that there are not duplicate
+          // classes listed
+          j--;
+        }
+      }
+      if (changed) {
+        el.attribs.class = elClasses.join(' ');
+      }
+    }
+  });
+};
+
+exports.toggleClass = function(value, stateVal) {
+  // Support functions
+  if (typeof value === 'function') {
+    return domEach(this, function(i, el) {
+      exports.toggleClass.call(
+        [el],
+        value.call(el, i, el.attribs['class'] || '', stateVal),
+        stateVal
+      );
+    });
+  }
+
+  // Return if no value or not a string or function
+  if (!value || typeof value !== 'string') return this;
+
+  var classNames = value.split(rspace),
+    numClasses = classNames.length,
+    state = typeof stateVal === 'boolean' ? stateVal ? 1 : -1 : 0,
+    numElements = this.length,
+    elementClasses,
+    index;
+
+  for (var i = 0; i < numElements; i++) {
+    // If selected element isn't a tag, move on
+    if (!isTag(this[i])) continue;
+
+    elementClasses = splitClass(this[i].attribs.class);
+
+    // Check if class already exists
+    for (var j = 0; j < numClasses; j++) {
+      // Check if the class name is currently defined
+      index = elementClasses.indexOf(classNames[j]);
+
+      // Add if stateValue === true or we are toggling and there is no value
+      if (state >= 0 && index < 0) {
+        elementClasses.push(classNames[j]);
+      } else if (state <= 0 && index >= 0) {
+        // Otherwise remove but only if the item exists
+        elementClasses.splice(index, 1);
+      }
+    }
+
+    this[i].attribs.class = elementClasses.join(' ');
+  }
+
+  return this;
+};
+
+exports.is = function (selector) {
+  if (selector) {
+    return this.filter(selector).length > 0;
+  }
+  return false;
+};
+

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/api/css.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/api/css.js 
b/node_modules/cheerio/lib/api/css.js
new file mode 100644
index 0000000..bfbdcda
--- /dev/null
+++ b/node_modules/cheerio/lib/api/css.js
@@ -0,0 +1,118 @@
+var _ = require('lodash'),
+    domEach = require('../utils').domEach;
+var toString = Object.prototype.toString;
+
+/**
+ * Set / Get css.
+ *
+ * @param {String|Object} prop
+ * @param {String} val
+ * @return {self}
+ * @api public
+ */
+
+exports.css = function(prop, val) {
+  if (arguments.length === 2 ||
+    // When `prop` is a "plain" object
+    (toString.call(prop) === '[object Object]')) {
+    return domEach(this, function(idx, el) {
+      setCss(el, prop, val, idx);
+    });
+  } else {
+    return getCss(this[0], prop);
+  }
+};
+
+/**
+ * Set styles of all elements.
+ *
+ * @param {String|Object} prop
+ * @param {String} val
+ * @param {Number} idx - optional index within the selection
+ * @return {self}
+ * @api private
+ */
+
+function setCss(el, prop, val, idx) {
+  if ('string' == typeof prop) {
+    var styles = getCss(el);
+    if (typeof val === 'function') {
+      val = val.call(el, idx, styles[prop]);
+    }
+
+    if (val === '') {
+      delete styles[prop];
+    } else if (val != null) {
+      styles[prop] = val;
+    }
+
+    el.attribs.style = stringify(styles);
+  } else if ('object' == typeof prop) {
+    Object.keys(prop).forEach(function(k){
+      setCss(el, k, prop[k]);
+    });
+  }
+}
+
+/**
+ * Get parsed styles of the first element.
+ *
+ * @param {String} prop
+ * @return {Object}
+ * @api private
+ */
+
+function getCss(el, prop) {
+  var styles = parse(el.attribs.style);
+  if (typeof prop === 'string') {
+    return styles[prop];
+  } else if (Array.isArray(prop)) {
+    return _.pick(styles, prop);
+  } else {
+    return styles;
+  }
+}
+
+/**
+ * Stringify `obj` to styles.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function stringify(obj) {
+  return Object.keys(obj || {})
+    .reduce(function(str, prop){
+      return str += ''
+        + (str ? ' ' : '')
+        + prop
+        + ': '
+        + obj[prop]
+        + ';';
+    }, '');
+}
+
+/**
+ * Parse `styles`.
+ *
+ * @param {String} styles
+ * @return {Object}
+ * @api private
+ */
+
+function parse(styles) {
+  styles = (styles || '').trim();
+
+  if (!styles) return {};
+
+  return styles
+    .split(';')
+    .reduce(function(obj, str){
+      var n = str.indexOf(':');
+      // skip if there is no :, or if it is the first/last character
+      if (n < 1 || n === str.length-1) return obj;
+      obj[str.slice(0,n).trim()] = str.slice(n+1).trim();
+      return obj;
+    }, {});
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/api/forms.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/api/forms.js 
b/node_modules/cheerio/lib/api/forms.js
new file mode 100644
index 0000000..9e335c9
--- /dev/null
+++ b/node_modules/cheerio/lib/api/forms.js
@@ -0,0 +1,49 @@
+// 
https://github.com/jquery/jquery/blob/2.1.3/src/manipulation/var/rcheckableType.js
+// https://github.com/jquery/jquery/blob/2.1.3/src/serialize.js
+var _ = require('lodash'),
+    submittableSelector = 'input,select,textarea,keygen',
+    rCRLF = /\r?\n/g;
+
+exports.serializeArray = function() {
+  // Resolve all form elements from either forms or collections of form 
elements
+  var Cheerio = this.constructor;
+  return this.map(function() {
+      var elem = this;
+      var $elem = Cheerio(elem);
+      if (elem.name === 'form') {
+        return $elem.find(submittableSelector).toArray();
+      } else {
+        return $elem.filter(submittableSelector).toArray();
+      }
+    }).filter(
+        // Verify elements have a name (`attr.name`) and are not disabled 
(`:disabled`)
+        '[name!=""]:not(:disabled)'
+        // and cannot be clicked (`[type=submit]`) or are used in 
`x-www-form-urlencoded` (`[type=file]`)
+        + ':not(:submit, :button, :image, :reset, :file)'
+        // and are either checked/don't have a checkable state
+        + ':matches([checked], :not(:checkbox, :radio))'
+    // Convert each of the elements to its value(s)
+    ).map(function(i, elem) {
+      var $elem = Cheerio(elem);
+      var name = $elem.attr('name');
+      var val = $elem.val();
+
+      // If there is no value set (e.g. `undefined`, `null`), then return 
nothing
+      if (val == null) {
+        return null;
+      } else {
+        // If we have an array of values (e.g. `<select multiple>`), return an 
array of key/value pairs
+        if (Array.isArray(val)) {
+          return _.map(val, function(val) {
+            // We trim replace any line endings (e.g. `\r` or `\r\n` with 
`\r\n`) to guarantee consistency across platforms
+            //   These can occur inside of `<textarea>'s`
+            return {name: name, value: val.replace( rCRLF, '\r\n' )};
+          });
+        // Otherwise (e.g. `<input type="text">`, return only one key/value 
pair
+        } else {
+          return {name: name, value: val.replace( rCRLF, '\r\n' )};
+        }
+      }
+    // Convert our result to an array
+    }).get();
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/api/manipulation.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/api/manipulation.js 
b/node_modules/cheerio/lib/api/manipulation.js
new file mode 100644
index 0000000..932b5da
--- /dev/null
+++ b/node_modules/cheerio/lib/api/manipulation.js
@@ -0,0 +1,421 @@
+var _ = require('lodash'),
+    parse = require('../parse'),
+    $ = require('../static'),
+    updateDOM = parse.update,
+    evaluate = parse.evaluate,
+    utils = require('../utils'),
+    domEach = utils.domEach,
+    cloneDom = utils.cloneDom,
+    isHtml = utils.isHtml,
+    slice = Array.prototype.slice;
+
+// Create an array of nodes, recursing into arrays and parsing strings if
+// necessary
+exports._makeDomArray = function makeDomArray(elem, clone) {
+  if (elem == null) {
+    return [];
+  } else if (elem.cheerio) {
+    return clone ? cloneDom(elem.get(), elem.options) : elem.get();
+  } else if (Array.isArray(elem)) {
+    return _.flatten(elem.map(function(el) {
+      return this._makeDomArray(el, clone);
+    }, this));
+  } else if (typeof elem === 'string') {
+    return evaluate(elem, this.options);
+  } else {
+    return clone ? cloneDom([elem]) : [elem];
+  }
+};
+
+var _insert = function(concatenator) {
+  return function() {
+    var elems = slice.call(arguments),
+        lastIdx = this.length - 1;
+
+    return domEach(this, function(i, el) {
+      var dom, domSrc;
+
+      if (typeof elems[0] === 'function') {
+        domSrc = elems[0].call(el, i, $.html(el.children));
+      } else {
+        domSrc = elems;
+      }
+
+      dom = this._makeDomArray(domSrc, i < lastIdx);
+      concatenator(dom, el.children, el);
+    });
+  };
+};
+
+/*
+ * Modify an array in-place, removing some number of elements and adding new
+ * elements directly following them.
+ *
+ * @param {Array} array Target array to splice.
+ * @param {Number} spliceIdx Index at which to begin changing the array.
+ * @param {Number} spliceCount Number of elements to remove from the array.
+ * @param {Array} newElems Elements to insert into the array.
+ *
+ * @api private
+ */
+var uniqueSplice = function(array, spliceIdx, spliceCount, newElems, parent) {
+  var spliceArgs = [spliceIdx, spliceCount].concat(newElems),
+      prev = array[spliceIdx - 1] || null,
+      next = array[spliceIdx] || null;
+  var idx, len, prevIdx, node, oldParent;
+
+  // Before splicing in new elements, ensure they do not already appear in the
+  // current array.
+  for (idx = 0, len = newElems.length; idx < len; ++idx) {
+    node = newElems[idx];
+    oldParent = node.parent || node.root;
+    prevIdx = oldParent && oldParent.children.indexOf(newElems[idx]);
+
+    if (oldParent && prevIdx > -1) {
+      oldParent.children.splice(prevIdx, 1);
+      if (parent === oldParent && spliceIdx > prevIdx) {
+        spliceArgs[0]--;
+      }
+    }
+
+    node.root = null;
+    node.parent = parent;
+
+    if (node.prev) {
+      node.prev.next = node.next || null;
+    }
+
+    if (node.next) {
+      node.next.prev = node.prev || null;
+    }
+
+    node.prev = newElems[idx - 1] || prev;
+    node.next = newElems[idx + 1] || next;
+  }
+
+  if (prev) {
+    prev.next = newElems[0];
+  }
+  if (next) {
+    next.prev = newElems[newElems.length - 1];
+  }
+  return array.splice.apply(array, spliceArgs);
+};
+
+exports.appendTo = function(target) {
+  if (!target.cheerio) {
+    target = this.constructor.call(this.constructor, target, null, 
this._originalRoot);
+  }
+
+  target.append(this);
+
+  return this;
+};
+
+exports.prependTo = function(target) {
+  if (!target.cheerio) {
+    target = this.constructor.call(this.constructor, target, null, 
this._originalRoot);
+  }
+
+  target.prepend(this);
+
+  return this;
+};
+
+exports.append = _insert(function(dom, children, parent) {
+  uniqueSplice(children, children.length, 0, dom, parent);
+});
+
+exports.prepend = _insert(function(dom, children, parent) {
+  uniqueSplice(children, 0, 0, dom, parent);
+});
+
+exports.wrap = function(wrapper) {
+  var wrapperFn = typeof wrapper === 'function' && wrapper,
+      lastIdx = this.length - 1;
+
+  _.forEach(this, _.bind(function(el, i) {
+    var parent = el.parent || el.root,
+        siblings = parent.children,
+        dom, index;
+
+    if (!parent) {
+      return;
+    }
+
+    if (wrapperFn) {
+      wrapper = wrapperFn.call(el, i);
+    }
+
+    if (typeof wrapper === 'string' && !isHtml(wrapper)) {
+      wrapper = this.parents().last().find(wrapper).clone();
+    }
+
+    dom = this._makeDomArray(wrapper, i < lastIdx).slice(0, 1);
+    index = siblings.indexOf(el);
+
+    updateDOM([el], dom[0]);
+    // The previous operation removed the current element from the `siblings`
+    // array, so the `dom` array can be inserted without removing any
+    // additional elements.
+    uniqueSplice(siblings, index, 0, dom, parent);
+  }, this));
+
+  return this;
+};
+
+exports.after = function() {
+  var elems = slice.call(arguments),
+      lastIdx = this.length - 1;
+
+  domEach(this, function(i, el) {
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        index = siblings.indexOf(el),
+        domSrc, dom;
+
+    // If not found, move on
+    if (index < 0) return;
+
+    if (typeof elems[0] === 'function') {
+      domSrc = elems[0].call(el, i, $.html(el.children));
+    } else {
+      domSrc = elems;
+    }
+    dom = this._makeDomArray(domSrc, i < lastIdx);
+
+    // Add element after `this` element
+    uniqueSplice(siblings, index + 1, 0, dom, parent);
+  });
+
+  return this;
+};
+
+exports.insertAfter = function(target) {
+  var clones = [],
+      self = this;
+  if (typeof target === 'string') {
+    target = this.constructor.call(this.constructor, target, null, 
this._originalRoot);
+  }
+  target = this._makeDomArray(target);
+  self.remove();
+  domEach(target, function(i, el) {
+    var clonedSelf = self._makeDomArray(self.clone());
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        index = siblings.indexOf(el);
+
+    // If not found, move on
+    if (index < 0) return;
+
+    // Add cloned `this` element(s) after target element
+    uniqueSplice(siblings, index + 1, 0, clonedSelf, parent);
+    clones.push(clonedSelf);
+  });
+  return this.constructor.call(this.constructor, this._makeDomArray(clones));
+};
+
+exports.before = function() {
+  var elems = slice.call(arguments),
+      lastIdx = this.length - 1;
+
+  domEach(this, function(i, el) {
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        index = siblings.indexOf(el),
+        domSrc, dom;
+
+    // If not found, move on
+    if (index < 0) return;
+
+    if (typeof elems[0] === 'function') {
+      domSrc = elems[0].call(el, i, $.html(el.children));
+    } else {
+      domSrc = elems;
+    }
+
+    dom = this._makeDomArray(domSrc, i < lastIdx);
+
+    // Add element before `el` element
+    uniqueSplice(siblings, index, 0, dom, parent);
+  });
+
+  return this;
+};
+
+exports.insertBefore = function(target) {
+  var clones = [],
+      self = this;
+  if (typeof target === 'string') {
+    target = this.constructor.call(this.constructor, target, null, 
this._originalRoot);
+  }
+  target = this._makeDomArray(target);
+  self.remove();
+  domEach(target, function(i, el) {
+    var clonedSelf = self._makeDomArray(self.clone());
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        index = siblings.indexOf(el);
+
+    // If not found, move on
+    if (index < 0) return;
+
+    // Add cloned `this` element(s) after target element
+    uniqueSplice(siblings, index, 0, clonedSelf, parent);
+    clones.push(clonedSelf);
+  });
+  return this.constructor.call(this.constructor, this._makeDomArray(clones));
+};
+
+/*
+  remove([selector])
+*/
+exports.remove = function(selector) {
+  var elems = this;
+
+  // Filter if we have selector
+  if (selector)
+    elems = elems.filter(selector);
+
+  domEach(elems, function(i, el) {
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        index = siblings.indexOf(el);
+
+    if (index < 0) return;
+
+    siblings.splice(index, 1);
+    if (el.prev) {
+      el.prev.next = el.next;
+    }
+    if (el.next) {
+      el.next.prev = el.prev;
+    }
+    el.prev = el.next = el.parent = el.root = null;
+  });
+
+  return this;
+};
+
+exports.replaceWith = function(content) {
+  var self = this;
+
+  domEach(this, function(i, el) {
+    var parent = el.parent || el.root;
+    if (!parent) {
+      return;
+    }
+
+    var siblings = parent.children,
+        dom = self._makeDomArray(typeof content === 'function' ? 
content.call(el, i, el) : content),
+        index;
+
+    // In the case that `dom` contains nodes that already exist in other
+    // structures, ensure those nodes are properly removed.
+    updateDOM(dom, null);
+
+    index = siblings.indexOf(el);
+
+    // Completely remove old element
+    uniqueSplice(siblings, index, 1, dom, parent);
+    el.parent = el.prev = el.next = el.root = null;
+  });
+
+  return this;
+};
+
+exports.empty = function() {
+  domEach(this, function(i, el) {
+    _.each(el.children, function(el) {
+      el.next = el.prev = el.parent = null;
+    });
+
+    el.children.length = 0;
+  });
+  return this;
+};
+
+/**
+ * Set/Get the HTML
+ */
+exports.html = function(str) {
+  if (str === undefined) {
+    if (!this[0] || !this[0].children) return null;
+    return $.html(this[0].children, this.options);
+  }
+
+  var opts = this.options;
+
+  domEach(this, function(i, el) {
+    _.each(el.children, function(el) {
+      el.next = el.prev = el.parent = null;
+    });
+
+    var content = str.cheerio ? str.clone().get() : evaluate('' + str, opts);
+
+    updateDOM(content, el);
+  });
+
+  return this;
+};
+
+exports.toString = function() {
+  return $.html(this, this.options);
+};
+
+exports.text = function(str) {
+  // If `str` is undefined, act as a "getter"
+  if (str === undefined) {
+    return $.text(this);
+  } else if (typeof str === 'function') {
+    // Function support
+    return domEach(this, function(i, el) {
+      var $el = [el];
+      return exports.text.call($el, str.call(el, i, $.text($el)));
+    });
+  }
+
+  // Append text node to each selected elements
+  domEach(this, function(i, el) {
+    _.each(el.children, function(el) {
+      el.next = el.prev = el.parent = null;
+    });
+
+    var elem = {
+      data: '' + str,
+      type: 'text',
+      parent: el,
+      prev: null,
+      next: null,
+      children: []
+    };
+
+    updateDOM(elem, el);
+  });
+
+  return this;
+};
+
+exports.clone = function() {
+  return this._make(cloneDom(this.get(), this.options));
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/api/traversing.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/api/traversing.js 
b/node_modules/cheerio/lib/api/traversing.js
new file mode 100644
index 0000000..b08d587
--- /dev/null
+++ b/node_modules/cheerio/lib/api/traversing.js
@@ -0,0 +1,423 @@
+var _ = require('lodash'),
+    select = require('css-select'),
+    utils = require('../utils'),
+    domEach = utils.domEach,
+    uniqueSort = require('htmlparser2').DomUtils.uniqueSort,
+    isTag = utils.isTag;
+
+exports.find = function(selectorOrHaystack) {
+  var elems = _.reduce(this, function(memo, elem) {
+    return memo.concat(_.filter(elem.children, isTag));
+  }, []);
+  var contains = this.constructor.contains;
+  var haystack;
+
+  if (selectorOrHaystack && typeof selectorOrHaystack !== 'string') {
+    if (selectorOrHaystack.cheerio) {
+      haystack = selectorOrHaystack.get();
+    } else {
+      haystack = [selectorOrHaystack];
+    }
+
+    return this._make(haystack.filter(function(elem) {
+      var idx, len;
+      for (idx = 0, len = this.length; idx < len; ++idx) {
+        if (contains(this[idx], elem)) {
+          return true;
+        }
+      }
+    }, this));
+  }
+
+  var options = {__proto__: this.options, context: this.toArray()};
+
+  return this._make(select(selectorOrHaystack, elems, options));
+};
+
+// Get the parent of each element in the current set of matched elements,
+// optionally filtered by a selector.
+exports.parent = function(selector) {
+  var set = [];
+
+  domEach(this, function(idx, elem) {
+    var parentElem = elem.parent;
+    if (parentElem && set.indexOf(parentElem) < 0) {
+      set.push(parentElem);
+    }
+  });
+
+  if (arguments.length) {
+    set = exports.filter.call(set, selector, this);
+  }
+
+  return this._make(set);
+};
+
+exports.parents = function(selector) {
+  var parentNodes = [];
+
+  // When multiple DOM elements are in the original set, the resulting set will
+  // be in *reverse* order of the original elements as well, with duplicates
+  // removed.
+  this.get().reverse().forEach(function(elem) {
+    traverseParents(this, elem.parent, selector, Infinity)
+      .forEach(function(node) {
+        if (parentNodes.indexOf(node) === -1) {
+          parentNodes.push(node);
+        }
+      }
+    );
+  }, this);
+
+  return this._make(parentNodes);
+};
+
+exports.parentsUntil = function(selector, filter) {
+  var parentNodes = [], untilNode, untilNodes;
+
+  if (typeof selector === 'string') {
+    untilNode = select(selector, this.parents().toArray(), this.options)[0];
+  } else if (selector && selector.cheerio) {
+    untilNodes = selector.toArray();
+  } else if (selector) {
+    untilNode = selector;
+  }
+
+  // When multiple DOM elements are in the original set, the resulting set will
+  // be in *reverse* order of the original elements as well, with duplicates
+  // removed.
+
+  this.toArray().reverse().forEach(function(elem) {
+    while ((elem = elem.parent)) {
+      if ((untilNode && elem !== untilNode) ||
+        (untilNodes && untilNodes.indexOf(elem) === -1) ||
+        (!untilNode && !untilNodes)) {
+        if (isTag(elem) && parentNodes.indexOf(elem) === -1) { 
parentNodes.push(elem); }
+      } else {
+        break;
+      }
+    }
+  }, this);
+
+  return this._make(filter ? select(filter, parentNodes, this.options) : 
parentNodes);
+};
+
+// For each element in the set, get the first element that matches the selector
+// by testing the element itself and traversing up through its ancestors in the
+// DOM tree.
+exports.closest = function(selector) {
+  var set = [];
+
+  if (!selector) {
+    return this._make(set);
+  }
+
+  domEach(this, function(idx, elem) {
+    var closestElem = traverseParents(this, elem, selector, 1)[0];
+
+    // Do not add duplicate elements to the set
+    if (closestElem && set.indexOf(closestElem) < 0) {
+      set.push(closestElem);
+    }
+  }.bind(this));
+
+  return this._make(set);
+};
+
+exports.next = function(selector) {
+  if (!this[0]) { return this; }
+  var elems = [];
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.next)) {
+      if (isTag(elem)) {
+        elems.push(elem);
+        return;
+      }
+    }
+  });
+
+  return selector ?
+    exports.filter.call(elems, selector, this) :
+    this._make(elems);
+};
+
+exports.nextAll = function(selector) {
+  if (!this[0]) { return this; }
+  var elems = [];
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.next)) {
+      if (isTag(elem) && elems.indexOf(elem) === -1) {
+        elems.push(elem);
+      }
+    }
+  });
+
+  return selector ?
+    exports.filter.call(elems, selector, this) :
+    this._make(elems);
+};
+
+exports.nextUntil = function(selector, filterSelector) {
+  if (!this[0]) { return this; }
+  var elems = [], untilNode, untilNodes;
+
+  if (typeof selector === 'string') {
+    untilNode = select(selector, this.nextAll().get(), this.options)[0];
+  } else if (selector && selector.cheerio) {
+    untilNodes = selector.get();
+  } else if (selector) {
+    untilNode = selector;
+  }
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.next)) {
+      if ((untilNode && elem !== untilNode) ||
+        (untilNodes && untilNodes.indexOf(elem) === -1) ||
+        (!untilNode && !untilNodes)) {
+        if (isTag(elem) && elems.indexOf(elem) === -1) {
+          elems.push(elem);
+        }
+      } else {
+        break;
+      }
+    }
+  });
+
+  return filterSelector ?
+    exports.filter.call(elems, filterSelector, this) :
+    this._make(elems);
+};
+
+exports.prev = function(selector) {
+  if (!this[0]) { return this; }
+  var elems = [];
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.prev)) {
+      if (isTag(elem)) {
+        elems.push(elem);
+        return;
+      }
+    }
+  });
+
+  return selector ?
+    exports.filter.call(elems, selector, this) :
+    this._make(elems);
+};
+
+exports.prevAll = function(selector) {
+  if (!this[0]) { return this; }
+  var elems = [];
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.prev)) {
+      if (isTag(elem) && elems.indexOf(elem) === -1) {
+        elems.push(elem);
+      }
+    }
+  });
+
+  return selector ?
+    exports.filter.call(elems, selector, this) :
+    this._make(elems);
+};
+
+exports.prevUntil = function(selector, filterSelector) {
+  if (!this[0]) { return this; }
+  var elems = [], untilNode, untilNodes;
+
+  if (typeof selector === 'string') {
+    untilNode = select(selector, this.prevAll().get(), this.options)[0];
+  } else if (selector && selector.cheerio) {
+    untilNodes = selector.get();
+  } else if (selector) {
+    untilNode = selector;
+  }
+
+  _.forEach(this, function(elem) {
+    while ((elem = elem.prev)) {
+      if ((untilNode && elem !== untilNode) ||
+        (untilNodes && untilNodes.indexOf(elem) === -1) ||
+        (!untilNode && !untilNodes)) {
+        if (isTag(elem) && elems.indexOf(elem) === -1) {
+          elems.push(elem);
+        }
+      } else {
+        break;
+      }
+    }
+  });
+
+  return filterSelector ?
+    exports.filter.call(elems, filterSelector, this) :
+    this._make(elems);
+};
+
+exports.siblings = function(selector) {
+  var parent = this.parent();
+
+  var elems = _.filter(
+    parent ? parent.children() : this.siblingsAndMe(),
+    _.bind(function(elem) { return isTag(elem) && !this.is(elem); }, this)
+  );
+
+  if (selector !== undefined) {
+    return exports.filter.call(elems, selector, this);
+  } else {
+    return this._make(elems);
+  }
+};
+
+exports.children = function(selector) {
+
+  var elems = _.reduce(this, function(memo, elem) {
+    return memo.concat(_.filter(elem.children, isTag));
+  }, []);
+
+  if (selector === undefined) return this._make(elems);
+
+  return exports.filter.call(elems, selector, this);
+};
+
+exports.contents = function() {
+  return this._make(_.reduce(this, function(all, elem) {
+    all.push.apply(all, elem.children);
+    return all;
+  }, []));
+};
+
+exports.each = function(fn) {
+  var i = 0, len = this.length;
+  while (i < len && fn.call(this[i], i, this[i]) !== false) ++i;
+  return this;
+};
+
+exports.map = function(fn) {
+  return this._make(_.reduce(this, function(memo, el, i) {
+    var val = fn.call(el, i, el);
+    return val == null ? memo : memo.concat(val);
+  }, []));
+};
+
+var makeFilterMethod = function(filterFn) {
+  return function(match, container) {
+    var testFn;
+    container = container || this;
+
+    if (typeof match === 'string') {
+      testFn = select.compile(match, container.options);
+    } else if (typeof match === 'function') {
+      testFn = function(el, i) {
+        return match.call(el, i, el);
+      };
+    } else if (match.cheerio) {
+      testFn = match.is.bind(match);
+    } else {
+      testFn = function(el) {
+        return match === el;
+      };
+    }
+
+    return container._make(filterFn(this, testFn));
+  };
+};
+
+exports.filter = makeFilterMethod(_.filter);
+exports.not = makeFilterMethod(_.reject);
+
+exports.has = function(selectorOrHaystack) {
+  var that = this;
+  return exports.filter.call(this, function() {
+    return that._make(this).find(selectorOrHaystack).length > 0;
+  });
+};
+
+exports.first = function() {
+  return this.length > 1 ? this._make(this[0]) : this;
+};
+
+exports.last = function() {
+  return this.length > 1 ? this._make(this[this.length - 1]) : this;
+};
+
+// Reduce the set of matched elements to the one at the specified index.
+exports.eq = function(i) {
+  i = +i;
+
+  // Use the first identity optimization if possible
+  if (i === 0 && this.length <= 1) return this;
+
+  if (i < 0) i = this.length + i;
+  return this[i] ? this._make(this[i]) : this._make([]);
+};
+
+// Retrieve the DOM elements matched by the jQuery object.
+exports.get = function(i) {
+  if (i == null) {
+    return Array.prototype.slice.call(this);
+  } else {
+    return this[i < 0 ? (this.length + i) : i];
+  }
+};
+
+// Search for a given element from among the matched elements.
+exports.index = function(selectorOrNeedle) {
+  var $haystack, needle;
+
+  if (arguments.length === 0) {
+    $haystack = this.parent().children();
+    needle = this[0];
+  } else if (typeof selectorOrNeedle === 'string') {
+    $haystack = this._make(selectorOrNeedle);
+    needle = this[0];
+  } else {
+    $haystack = this;
+    needle = selectorOrNeedle.cheerio ? selectorOrNeedle[0] : selectorOrNeedle;
+  }
+
+  return $haystack.get().indexOf(needle);
+};
+
+exports.slice = function() {
+  return this._make([].slice.apply(this, arguments));
+};
+
+function traverseParents(self, elem, selector, limit) {
+  var elems = [];
+  while (elem && elems.length < limit) {
+    if (!selector || exports.filter.call([elem], selector, self).length) {
+      elems.push(elem);
+    }
+    elem = elem.parent;
+  }
+  return elems;
+}
+
+// End the most recent filtering operation in the current chain and return the
+// set of matched elements to its previous state.
+exports.end = function() {
+  return this.prevObject || this._make([]);
+};
+
+exports.add = function(other, context) {
+  var selection = this._make(other, context);
+  var contents = uniqueSort(selection.get().concat(this.get()));
+
+  for (var i = 0; i < contents.length; ++i) {
+    selection[i] = contents[i];
+  }
+  selection.length = contents.length;
+
+  return selection;
+};
+
+// Add the previous set of elements on the stack to the current set, optionally
+// filtered by a selector.
+exports.addBack = function(selector) {
+  return this.add(
+    arguments.length ? this.prevObject.filter(selector) : this.prevObject
+  );
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/cheerio.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/cheerio.js 
b/node_modules/cheerio/lib/cheerio.js
new file mode 100644
index 0000000..5b1156a
--- /dev/null
+++ b/node_modules/cheerio/lib/cheerio.js
@@ -0,0 +1,143 @@
+/*
+  Module dependencies
+*/
+
+var parse = require('./parse'),
+    isHtml = require('./utils').isHtml,
+    _ = require('lodash');
+
+/*
+ * The API
+ */
+
+var api = [
+  require('./api/attributes'),
+  require('./api/traversing'),
+  require('./api/manipulation'),
+  require('./api/css'),
+  require('./api/forms')
+];
+
+/*
+ * Instance of cheerio
+ */
+
+var Cheerio = module.exports = function(selector, context, root, options) {
+  if (!(this instanceof Cheerio)) return new Cheerio(selector, context, root, 
options);
+
+  this.options = _.defaults(options || {}, this.options);
+
+  // $(), $(null), $(undefined), $(false)
+  if (!selector) return this;
+
+  if (root) {
+    if (typeof root === 'string') root = parse(root, this.options);
+    this._root = Cheerio.call(this, root);
+  }
+
+  // $($)
+  if (selector.cheerio) return selector;
+
+  // $(dom)
+  if (isNode(selector))
+    selector = [selector];
+
+  // $([dom])
+  if (Array.isArray(selector)) {
+    _.forEach(selector, _.bind(function(elem, idx) {
+      this[idx] = elem;
+    }, this));
+    this.length = selector.length;
+    return this;
+  }
+
+  // $(<html>)
+  if (typeof selector === 'string' && isHtml(selector)) {
+    return Cheerio.call(this, parse(selector, this.options).children);
+  }
+
+  // If we don't have a context, maybe we have a root, from loading
+  if (!context) {
+    context = this._root;
+  } else if (typeof context === 'string') {
+    if (isHtml(context)) {
+      // $('li', '<ul>...</ul>')
+      context = parse(context, this.options);
+      context = Cheerio.call(this, context);
+    } else {
+      // $('li', 'ul')
+      selector = [context, selector].join(' ');
+      context = this._root;
+    }
+  // $('li', node), $('li', [nodes])
+  } else if (!context.cheerio) {
+    context = Cheerio.call(this, context);
+  }
+
+  // If we still don't have a context, return
+  if (!context) return this;
+
+  // #id, .class, tag
+  return context.find(selector);
+};
+
+/**
+ * Mix in `static`
+ */
+
+_.extend(Cheerio, require('./static'));
+
+/*
+ * Set a signature of the object
+ */
+
+Cheerio.prototype.cheerio = '[cheerio object]';
+
+/*
+ * Cheerio default options
+ */
+
+Cheerio.prototype.options = {
+  withDomLvl1: true,
+  normalizeWhitespace: false,
+  xmlMode: false,
+  decodeEntities: true
+};
+
+/*
+ * Make cheerio an array-like object
+ */
+
+Cheerio.prototype.length = 0;
+Cheerio.prototype.splice = Array.prototype.splice;
+
+/*
+ * Make a cheerio object
+ *
+ * @api private
+ */
+
+Cheerio.prototype._make = function(dom, context) {
+  var cheerio = new this.constructor(dom, context, this._root, this.options);
+  cheerio.prevObject = this;
+  return cheerio;
+};
+
+/**
+ * Turn a cheerio object into an array
+ */
+
+Cheerio.prototype.toArray = function() {
+  return this.get();
+};
+
+/**
+ * Plug in the API
+ */
+api.forEach(function(mod) {
+  _.extend(Cheerio.prototype, mod);
+});
+
+var isNode = function(obj) {
+  return obj.name || obj.type === 'text' || obj.type === 'comment';
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/parse.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/parse.js 
b/node_modules/cheerio/lib/parse.js
new file mode 100644
index 0000000..b06245d
--- /dev/null
+++ b/node_modules/cheerio/lib/parse.js
@@ -0,0 +1,86 @@
+/*
+  Module Dependencies
+*/
+var htmlparser = require('htmlparser2');
+
+/*
+  Parser
+*/
+exports = module.exports = function(content, options) {
+  var dom = exports.evaluate(content, options),
+      // Generic root element
+      root = exports.evaluate('<root></root>', options)[0];
+
+  root.type = 'root';
+
+  // Update the dom using the root
+  exports.update(dom, root);
+
+  return root;
+};
+
+exports.evaluate = function(content, options) {
+  // options = options || $.fn.options;
+
+  var dom;
+
+  if (typeof content === 'string' || Buffer.isBuffer(content)) {
+    dom = htmlparser.parseDOM(content, options);
+  } else {
+    dom = content;
+  }
+
+  return dom;
+};
+
+/*
+  Update the dom structure, for one changed layer
+*/
+exports.update = function(arr, parent) {
+  // normalize
+  if (!Array.isArray(arr)) arr = [arr];
+
+  // Update parent
+  if (parent) {
+    parent.children = arr;
+  } else {
+    parent = null;
+  }
+
+  // Update neighbors
+  for (var i = 0; i < arr.length; i++) {
+    var node = arr[i];
+
+    // Cleanly remove existing nodes from their previous structures.
+    var oldParent = node.parent || node.root,
+        oldSiblings = oldParent && oldParent.children;
+    if (oldSiblings && oldSiblings !== arr) {
+      oldSiblings.splice(oldSiblings.indexOf(node), 1);
+      if (node.prev) {
+        node.prev.next = node.next;
+      }
+      if (node.next) {
+        node.next.prev = node.prev;
+      }
+    }
+
+    if (parent) {
+      node.prev = arr[i - 1] || null;
+      node.next = arr[i + 1] || null;
+    } else {
+      node.prev = node.next = null;
+    }
+
+    if (parent && parent.type === 'root') {
+      node.root = parent;
+      node.parent = null;
+    } else {
+      node.root = null;
+      node.parent = parent;
+    }
+  }
+
+  return parent;
+};
+
+// module.exports = $.extend(exports);

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/static.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/static.js 
b/node_modules/cheerio/lib/static.js
new file mode 100644
index 0000000..88f9536
--- /dev/null
+++ b/node_modules/cheerio/lib/static.js
@@ -0,0 +1,182 @@
+/**
+ * Module dependencies
+ */
+
+var select = require('css-select'),
+    parse = require('./parse'),
+    serialize = require('dom-serializer'),
+    _ = require('lodash');
+
+/**
+ * $.load(str)
+ */
+
+exports.load = function(content, options) {
+  var Cheerio = require('./cheerio');
+
+  options = _.defaults(options || {}, Cheerio.prototype.options);
+
+  var root = parse(content, options);
+
+  var initialize = function(selector, context, r, opts) {
+    if (!(this instanceof initialize)) {
+      return new initialize(selector, context, r, opts);
+    }
+    opts = _.defaults(opts || {}, options);
+    return Cheerio.call(this, selector, context, r || root, opts);
+  };
+
+  // Ensure that selections created by the "loaded" `initialize` function are
+  // true Cheerio instances.
+  initialize.prototype = Object.create(Cheerio.prototype);
+  initialize.prototype.constructor = initialize;
+
+  // Mimic jQuery's prototype alias for plugin authors.
+  initialize.fn = initialize.prototype;
+
+  // Keep a reference to the top-level scope so we can chain methods that 
implicitly 
+  // resolve selectors; e.g. $("<span>").(".bar"), which otherwise loses ._root
+  initialize.prototype._originalRoot = root;
+
+  // Add in the static methods
+  _.merge(initialize, exports);
+
+  // Add in the root
+  initialize._root = root;
+  // store options
+  initialize._options = options;
+
+  return initialize;
+};
+
+/*
+* Helper function
+*/
+
+function render(that, dom, options) {
+  if (!dom) {
+    if (that._root && that._root.children) {
+      dom = that._root.children;
+    } else {
+      return '';
+    }
+  } else if (typeof dom === 'string') {
+    dom = select(dom, that._root, options);
+  }
+
+  return serialize(dom, options);
+}
+
+/**
+ * $.html([selector | dom], [options])
+ */
+
+exports.html = function(dom, options) {
+  var Cheerio = require('./cheerio');
+
+  // be flexible about parameters, sometimes we call html(),
+  // with options as only parameter
+  // check dom argument for dom element specific properties
+  // assume there is no 'length' or 'type' properties in the options object
+  if (Object.prototype.toString.call(dom) === '[object Object]' && !options && 
!('length' in dom) && !('type' in dom))
+  {
+    options = dom;
+    dom = undefined;
+  }
+
+  // sometimes $.html() used without preloading html
+  // so fallback non existing options to the default ones
+  options = _.defaults(options || {}, this._options, 
Cheerio.prototype.options);
+
+  return render(this, dom, options);
+};
+
+/**
+ * $.xml([selector | dom])
+ */
+
+exports.xml = function(dom) {
+  var options = _.defaults({xmlMode: true}, this._options);
+
+  return render(this, dom, options);
+};
+
+/**
+ * $.text(dom)
+ */
+
+exports.text = function(elems) {
+  if (!elems) return '';
+
+  var ret = '',
+      len = elems.length,
+      elem;
+
+  for (var i = 0; i < len; i++) {
+    elem = elems[i];
+    if (elem.type === 'text') ret += elem.data;
+    else if (elem.children && elem.type !== 'comment') {
+      ret += exports.text(elem.children);
+    }
+  }
+
+  return ret;
+};
+
+/**
+ * $.parseHTML(data [, context ] [, keepScripts ])
+ * Parses a string into an array of DOM nodes. The `context` argument has no
+ * meaning for Cheerio, but it is maintained for API compatibility with jQuery.
+ */
+exports.parseHTML = function(data, context, keepScripts) {
+  var parsed;
+
+  if (!data || typeof data !== 'string') {
+    return null;
+  }
+
+  if (typeof context === 'boolean') {
+    keepScripts = context;
+  }
+
+  parsed = this.load(data);
+  if (!keepScripts) {
+    parsed('script').remove();
+  }
+
+  // The `children` array is used by Cheerio internally to group elements that
+  // share the same parents. When nodes created through `parseHTML` are
+  // inserted into previously-existing DOM structures, they will be removed
+  // from the `children` array. The results of `parseHTML` should remain
+  // constant across these operations, so a shallow copy should be returned.
+  return parsed.root()[0].children.slice();
+};
+
+/**
+ * $.root()
+ */
+exports.root = function() {
+  return this(this._root);
+};
+
+/**
+ * $.contains()
+ */
+exports.contains = function(container, contained) {
+
+  // According to the jQuery API, an element does not "contain" itself
+  if (contained === container) {
+    return false;
+  }
+
+  // Step up the descendants, stopping when the root element is reached
+  // (signaled by `.parent` returning a reference to the same object)
+  while (contained && contained !== contained.parent) {
+    contained = contained.parent;
+    if (contained === container) {
+      return true;
+    }
+  }
+
+  return false;
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/lib/utils.js
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/lib/utils.js 
b/node_modules/cheerio/lib/utils.js
new file mode 100644
index 0000000..baa62fc
--- /dev/null
+++ b/node_modules/cheerio/lib/utils.js
@@ -0,0 +1,83 @@
+var parse = require('./parse'),
+    render = require('dom-serializer');
+
+/**
+ * HTML Tags
+ */
+
+var tags = { tag: true, script: true, style: true };
+
+/**
+ * Check if the DOM element is a tag
+ *
+ * isTag(type) includes <script> and <style> tags
+ */
+
+exports.isTag = function(type) {
+  if (type.type) type = type.type;
+  return tags[type] || false;
+};
+
+/**
+ * Convert a string to camel case notation.
+ * @param  {String} str String to be converted.
+ * @return {String}     String in camel case notation.
+ */
+
+exports.camelCase = function(str) {
+  return str.replace(/[_.-](\w|$)/g, function(_, x) {
+    return x.toUpperCase();
+  });
+};
+
+/**
+ * Convert a string from camel case to "CSS case", where word boundaries are
+ * described by hyphens ("-") and all characters are lower-case.
+ * @param  {String} str String to be converted.
+ * @return {string}     String in "CSS case".
+ */
+exports.cssCase = function(str) {
+  return str.replace(/[A-Z]/g, '-$&').toLowerCase();
+};
+
+/**
+ * Iterate over each DOM element without creating intermediary Cheerio 
instances.
+ *
+ * This is indented for use internally to avoid otherwise unnecessary memory 
pressure introduced
+ * by _make.
+ */
+
+exports.domEach = function(cheerio, fn) {
+  var i = 0, len = cheerio.length;
+  while (i < len && fn.call(cheerio, i, cheerio[i]) !== false) ++i;
+  return cheerio;
+};
+
+/**
+ * Create a deep copy of the given DOM structure by first rendering it to a
+ * string and then parsing the resultant markup.
+ *
+ * @argument {Object} dom - The htmlparser2-compliant DOM structure
+ * @argument {Object} options - The parsing/rendering options
+ */
+exports.cloneDom = function(dom, options) {
+  return parse(render(dom, options), options).children;
+};
+
+/*
+ * A simple way to check for HTML strings or ID strings
+ */
+
+var quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
+
+/*
+ * Check if string is HTML
+ */
+exports.isHtml = function(str) {
+  // Faster than running regex, if str starts with `<` and ends with `>`, 
assume it's HTML
+  if (str.charAt(0) === '<' && str.charAt(str.length - 1) === '>' && 
str.length >= 3) return true;
+
+  // Run the regex
+  var match = quickExpr.exec(str);
+  return !!(match && match[1]);
+};

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/cheerio/package.json
----------------------------------------------------------------------
diff --git a/node_modules/cheerio/package.json 
b/node_modules/cheerio/package.json
new file mode 100644
index 0000000..92ef762
--- /dev/null
+++ b/node_modules/cheerio/package.json
@@ -0,0 +1,126 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "cheerio@^0.20.0",
+        "scope": null,
+        "escapedName": "cheerio",
+        "name": "cheerio",
+        "rawSpec": "^0.20.0",
+        "spec": ">=0.20.0 <0.21.0",
+        "type": "range"
+      },
+      "/Users/yueguo/tmp/griffin-site/node_modules/hexo"
+    ]
+  ],
+  "_from": "cheerio@>=0.20.0 <0.21.0",
+  "_id": "cheerio@0.20.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/cheerio",
+  "_nodeVersion": "5.5.0",
+  "_npmUser": {
+    "name": "feedic",
+    "email": "m...@feedic.com"
+  },
+  "_npmVersion": "3.6.0",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "cheerio@^0.20.0",
+    "scope": null,
+    "escapedName": "cheerio",
+    "name": "cheerio",
+    "rawSpec": "^0.20.0",
+    "spec": ">=0.20.0 <0.21.0",
+    "type": "range"
+  },
+  "_requiredBy": [
+    "/hexo"
+  ],
+  "_resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz";,
+  "_shasum": "5c710f2bab95653272842ba01c6ea61b3545ec35",
+  "_shrinkwrap": null,
+  "_spec": "cheerio@^0.20.0",
+  "_where": "/Users/yueguo/tmp/griffin-site/node_modules/hexo",
+  "author": {
+    "name": "Matt Mueller",
+    "email": "mattmue...@gmail.com",
+    "url": "mat.io"
+  },
+  "bugs": {
+    "url": "https://github.com/cheeriojs/cheerio/issues";
+  },
+  "dependencies": {
+    "css-select": "~1.2.0",
+    "dom-serializer": "~0.1.0",
+    "entities": "~1.1.1",
+    "htmlparser2": "~3.8.1",
+    "jsdom": "^7.0.2",
+    "lodash": "^4.1.0"
+  },
+  "description": "Tiny, fast, and elegant implementation of core jQuery 
designed specifically for the server",
+  "devDependencies": {
+    "benchmark": "~1.0.0",
+    "coveralls": "~2.10",
+    "expect.js": "~0.3.1",
+    "istanbul": "~0.2",
+    "jshint": "~2.5.1",
+    "mocha": "*",
+    "xyz": "~0.5.0"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "5c710f2bab95653272842ba01c6ea61b3545ec35",
+    "tarball": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz";
+  },
+  "engines": {
+    "node": ">= 0.6"
+  },
+  "files": [
+    "index.js",
+    "lib"
+  ],
+  "gitHead": "c3ec1cd7bff41da0033bdc45375d77844f0f81c0",
+  "homepage": "https://github.com/cheeriojs/cheerio#readme";,
+  "keywords": [
+    "htmlparser",
+    "jquery",
+    "selector",
+    "scraper",
+    "parser",
+    "html"
+  ],
+  "license": "MIT",
+  "main": "./index.js",
+  "maintainers": [
+    {
+      "name": "mattmueller",
+      "email": "mattmue...@gmail.com"
+    },
+    {
+      "name": "davidchambers",
+      "email": "d...@davidchambers.me"
+    },
+    {
+      "name": "jugglinmike",
+      "email": "m...@mikepennisi.com"
+    },
+    {
+      "name": "feedic",
+      "email": "m...@feedic.com"
+    }
+  ],
+  "name": "cheerio",
+  "optionalDependencies": {
+    "jsdom": "^7.0.2"
+  },
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/cheeriojs/cheerio.git"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "0.20.0"
+}

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/chokidar/CHANGELOG.md
----------------------------------------------------------------------
diff --git a/node_modules/chokidar/CHANGELOG.md 
b/node_modules/chokidar/CHANGELOG.md
new file mode 100644
index 0000000..0874dee
--- /dev/null
+++ b/node_modules/chokidar/CHANGELOG.md
@@ -0,0 +1,268 @@
+# Chokidar 1.6.0 (Jun 22, 2016)
+* Added ability for force `usePolling` mode by setting `CHOKIDAR_USEPOLLING`
+  env variable
+
+# Chokidar 1.5.2 (Jun 7, 2016)
+* Fix missing `addDir` events when using `cwd` and `alwaysStat` options
+* Fix missing `add` events for files within a renamed directory
+
+# Chokidar 1.5.1 (May 20, 2016)
+* To help prevent exhaustion of FSEvents system limitations, consolidate watch
+  instances to the common parent upon detection of separate watch instances on
+  many siblings
+
+# Chokidar 1.5.0 (May 10, 2016)
+* Make debounce delay setting used with `atomic: true` user-customizable
+* Fixes and improvements to `awaitWriteFinish` features
+
+# Chokidar 1.4.3 (Feb 26, 2016)
+* Update async-each dependency to ^1.0.0
+
+# Chokidar 1.4.2 (Dec 30, 2015)
+* Now correctly emitting `stats` with `awaitWriteFinish` option.
+
+# Chokidar 1.4.1 (Dec 9, 2015)
+* The watcher could now be correctly subclassed with ES6 class syntax.
+
+# Chokidar 1.4.0 (Dec 3, 2015)
+* Add `.getWatched()` method, exposing all file system entries being watched
+* Apply `awaitWriteFinish` methodology to `change` events (in addition to 
`add`)
+* Fix handling of symlinks within glob paths (#293)
+* Fix `addDir` and `unlinkDir` events under globs (#337, #401)
+* Fix issues with `.unwatch()` (#374, #403)
+
+# Chokidar 1.3.0 (Nov 18, 2015)
+* Improve `awaitWriteFinish` option behavior
+* Fix some `cwd` option behavior on Windows
+* `awaitWriteFinish` and `cwd` are now compatible
+* Fix some race conditions.
+* #379: Recreating deleted directory doesn't trigger event
+* When adding a previously-deleted file, emit 'add', not 'change'
+
+# Chokidar 1.2.0 (Oct 1, 2015)
+* Allow nested arrays of paths to be provided to `.watch()` and `.add()`
+* Add `awaitWriteFinish` option
+
+# Chokidar 1.1.0 (Sep 23, 2015)
+* Dependency updates including fsevents@1.0.0, improving installation
+
+# Chokidar 1.0.6 (Sep 18, 2015)
+* Fix issue with `.unwatch()` method and relative paths
+
+# Chokidar 1.0.5 (Jul 20, 2015)
+* Fix regression with regexes/fns using in `ignored`
+
+# Chokidar 1.0.4 (Jul 15, 2015)
+* Fix bug with `ignored` files/globs while `cwd` option is set
+
+# Chokidar 1.0.3 (Jun 4, 2015)
+* Fix race issue with `alwaysStat` option and removed files
+
+# Chokidar 1.0.2 (May 30, 2015)
+* Fix bug with absolute paths and ENAMETOOLONG error
+
+# Chokidar 1.0.1 (Apr 8, 2015)
+* Fix bug with `.close()` method in `fs.watch` mode with `persistent: false`
+  option
+
+# Chokidar 1.0.0 (Apr 7, 2015)
+* Glob support! Use globs in `watch`, `add`, and `unwatch` methods
+* Comprehensive symlink support
+* New `unwatch` method to turn off watching of previously watched paths
+* More flexible `ignored` option allowing regex, function, glob, or array
+  courtesy of [anymatch](https://github.com/es128/anymatch)
+* New `cwd` option to set base dir from which relative paths are derived
+* New `depth` option for limiting recursion
+* New `alwaysStat` option to ensure
+  [`fs.Stats`](https://nodejs.org/api/fs.html#fs_class_fs_stats) gets passed
+  with every add/change event
+* New `ready` event emitted when initial fs tree scan is done and watcher is
+  ready for changes
+* New `raw` event exposing data and events from the lower-level watch modules
+* New `followSymlinks` option to impact whether symlinks' targets or the 
symlink
+  files themselves are watched
+* New `atomic` option for normalizing artifacts from text editors that use
+  atomic write methods
+* Ensured watcher's stability with lots of bugfixes.
+
+# Chokidar 0.12.6 (Jan 6, 2015)
+* Fix bug which breaks `persistent: false` mode when change events occur
+
+# Chokidar 0.12.5 (Dec 17, 2014)
+* Fix bug with matching parent path detection for fsevents instance sharing
+* Fix bug with ignored watch path in nodefs modes
+
+# Chokidar 0.12.4 (Dec 14, 2014)
+* Fix bug in `fs.watch` mode that caused watcher to leak into `cwd`
+* Fix bug preventing ready event when there are symlinks to ignored paths
+
+# Chokidar 0.12.3 (Dec 13, 2014)
+* Fix handling of special files such as named pipes and sockets
+
+# Chokidar 0.12.2 (Dec 12, 2014)
+* Fix recursive symlink handling and some other path resolution problems
+
+# Chokidar 0.12.1 (Dec 10, 2014)
+* Fix a case where file symlinks were not followed properly
+
+# Chokidar 0.12.0 (Dec 8, 2014)
+* Symlink support
+  * Add `followSymlinks` option, which defaults to `true`
+* Change default watch mode on Linux to non-polling `fs.watch`
+* Add `atomic` option to normalize events from editors using atomic writes
+  * Particularly Vim and Sublime
+* Add `raw` event which exposes data from the underlying watch method
+
+# Chokidar 0.11.1 (Nov 19, 2014)
+* Fix a bug where an error is thrown when `fs.watch` instantiation fails
+
+# Chokidar 0.11.0 (Nov 16, 2014)
+* Add a `ready` event, which is emitted after initial file scan completes
+* Fix issue with options keys passed in defined as `undefined`
+* Rename some internal `FSWatcher` properties to indicate they're private
+
+# Chokidar 0.10.9 (Nov 15, 2014)
+* Fix some leftover issues from adding watcher reuse
+
+# Chokidar 0.10.8 (Nov 14, 2014)
+* Remove accidentally committed/published `console.log` statement.
+* Sry 'bout that :crying_cat_face:
+
+# Chokidar 0.10.7 (Nov 14, 2014)
+* Apply watcher reuse methodology to `fs.watch` and `fs.watchFile` as well
+
+# Chokidar 0.10.6 (Nov 12, 2014)
+* More efficient creation/reuse of FSEvents instances to avoid system limits
+* Reduce simultaneous FSEvents instances allowed in a process
+* Handle errors thrown by `fs.watch` upon invocation
+
+# Chokidar 0.10.5 (Nov 6, 2014)
+* Limit number of simultaneous FSEvents instances (fall back to other methods)
+* Prevent some cases of EMFILE errors during initialization
+* Fix ignored files emitting events in some fsevents-mode circumstances
+
+# Chokidar 0.10.4 (Nov 5, 2014)
+* Bump fsevents dependency to ~0.3.1
+  * Should resolve build warnings and `npm rebuild` on non-Macs
+
+# Chokidar 0.10.3 (Oct 28, 2014)
+* Fix removed dir emitting as `unlink` instead of `unlinkDir`
+* Fix issues with file changing to dir or vice versa (gh-165)
+* Fix handling of `ignored` option in fsevents mode
+
+# Chokidar 0.10.2 (Oct 23, 2014)
+* Improve individual file watching
+* Fix fsevents keeping process alive when `persistent: false`
+
+# Chokidar 0.10.1 (19 October 2014)
+* Improve handling of text editor atomic writes
+
+# Chokidar 0.10.0 (Oct 18, 2014)
+* Many stability and consistency improvements
+* Resolve many cases of duplicate or wrong events
+* Correct for fsevents inconsistencies
+* Standardize handling of errors and relative paths
+* Fix issues with watching `./`
+
+# Chokidar 0.9.0 (Sep 25, 2014)
+* Updated fsevents to 0.3
+* Update per-system defaults
+* Fix issues with closing chokidar instance
+* Fix duplicate change events on win32
+
+# Chokidar 0.8.2 (Mar 26, 2014)
+* Fixed npm issues related to fsevents dep.
+* Updated fsevents to 0.2.
+
+# Chokidar 0.8.1 (Dec 16, 2013)
+* Optional deps are now truly optional on windows and
+  linux.
+* Rewritten in JS, again.
+* Fixed some FSEvents-related bugs.
+
+# Chokidar 0.8.0 (Nov 29, 2013)
+* Added ultra-fast low-CPU OS X file watching with FSEvents.
+  It is enabled by default.
+* Added `addDir` and `unlinkDir` events.
+* Polling is now disabled by default on all platforms.
+
+# Chokidar 0.7.1 (Nov 18, 2013)
+* `Watcher#close` now also removes all event listeners.
+
+# Chokidar 0.7.0 (Oct 22, 2013)
+* When `options.ignored` is two-argument function, it will
+  also be called after stating the FS, with `stats` argument.
+* `unlink` is no longer emitted on directories.
+
+# Chokidar 0.6.3 (Aug 12, 2013)
+* Added `usePolling` option (default: `true`).
+  When `false`, chokidar will use `fs.watch` as backend.
+  `fs.watch` is much faster, but not like super reliable.
+
+# Chokidar 0.6.2 (Mar 19, 2013)
+* Fixed watching initially empty directories with `ignoreInitial` option.
+
+# Chokidar 0.6.1 (Mar 19, 2013)
+* Added node.js 0.10 support.
+
+# Chokidar 0.6.0 (Mar 10, 2013)
+* File attributes (stat()) are now passed to `add` and `change` events as 
second
+  arguments.
+* Changed default polling interval for binary files to 300ms.
+
+# Chokidar 0.5.3 (Jan 13, 2013)
+* Removed emitting of `change` events before `unlink`.
+
+# Chokidar 0.5.2 (Jan 13, 2013)
+* Removed postinstall script to prevent various npm bugs.
+
+# Chokidar 0.5.1 (Jan 6, 2013)
+* When starting to watch non-existing paths, chokidar will no longer throw
+  ENOENT error.
+* Fixed bug with absolute path.
+
+# Chokidar 0.5.0 (Dec 9, 2012)
+* Added a bunch of new options:
+    * `ignoreInitial` that allows to ignore initial `add` events.
+    * `ignorePermissionErrors` that allows to ignore ENOENT etc perm errors.
+    * `interval` and `binaryInterval` that allow to change default
+    fs polling intervals.
+
+# Chokidar 0.4.0 (Jul 26, 2012)
+* Added `all` event that receives two args (event name and path) that combines
+  `add`, `change` and `unlink` events.
+* Switched to `fs.watchFile` on node.js 0.8 on windows.
+* Files are now correctly unwatched after unlink.
+
+# Chokidar 0.3.0 (Jun 24, 2012)
+* `unlink` event are no longer emitted for directories, for consistency with
+  `add`.
+
+# Chokidar 0.2.6 (Jun 8, 2012)
+* Prevented creating of duplicate 'add' events.
+
+# Chokidar 0.2.5 (Jun 8, 2012)
+* Fixed a bug when new files in new directories hadn't been added.
+
+# Chokidar 0.2.4 (Jun 7, 2012)
+* Fixed a bug when unlinked files emitted events after unlink.
+
+# Chokidar 0.2.3 (May 12, 2012)
+* Fixed watching of files on windows.
+
+# Chokidar 0.2.2 (May 4, 2012)
+* Fixed watcher signature.
+
+# Chokidar 0.2.1 (May 4, 2012)
+* Fixed invalid API bug when using `watch()`.
+
+# Chokidar 0.2.0 (May 4, 2012)
+* Rewritten in js.
+
+# Chokidar 0.1.1 (Apr 26, 2012)
+* Changed api to `chokidar.watch()`.
+* Fixed compilation on windows.
+
+# Chokidar 0.1.0 (Apr 20, 2012)
+* Initial release, extracted from
+  
[Brunch](https://github.com/brunch/brunch/blob/9847a065aea300da99bd0753f90354cde9de1261/src/helpers.coffee#L66)

http://git-wip-us.apache.org/repos/asf/incubator-griffin-site/blob/4f8fa326/node_modules/chokidar/README.md
----------------------------------------------------------------------
diff --git a/node_modules/chokidar/README.md b/node_modules/chokidar/README.md
new file mode 100644
index 0000000..bcd005b
--- /dev/null
+++ b/node_modules/chokidar/README.md
@@ -0,0 +1,289 @@
+# Chokidar [![Mac/Linux Build 
Status](https://img.shields.io/travis/paulmillr/chokidar/master.svg?label=Mac%20OSX%20%26%20Linux)](https://travis-ci.org/paulmillr/chokidar)
 [![Windows Build 
status](https://img.shields.io/appveyor/ci/es128/chokidar/master.svg?label=Windows)](https://ci.appveyor.com/project/es128/chokidar/branch/master)
 [![Coverage 
Status](https://coveralls.io/repos/paulmillr/chokidar/badge.svg)](https://coveralls.io/r/paulmillr/chokidar)
 [![Join the chat at 
https://gitter.im/paulmillr/chokidar](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paulmillr/chokidar?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+> A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.
+
+[![NPM](https://nodei.co/npm-dl/chokidar.png)](https://nodei.co/npm/chokidar/)
+[![NPM](https://nodei.co/npm/chokidar.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/chokidar/)
+
+## Why?
+Node.js `fs.watch`:
+
+* Doesn't report filenames on OS X.
+* Doesn't report events at all when using editors like Sublime on OS X.
+* Often reports events twice.
+* Emits most changes as `rename`.
+* Has [a lot of other 
issues](https://github.com/joyent/node/search?q=fs.watch&type=Issues)
+* Does not provide an easy way to recursively watch file trees.
+
+Node.js `fs.watchFile`:
+
+* Almost as bad at event handling.
+* Also does not provide any recursive watching.
+* Results in high CPU utilization.
+
+Chokidar resolves these problems.
+
+Initially made for [brunch](http://brunch.io) (an ultra-swift web app build 
tool), it is now used in
+[gulp](https://github.com/gulpjs/gulp/),
+[karma](http://karma-runner.github.io),
+[PM2](https://github.com/Unitech/PM2),
+[browserify](http://browserify.org/),
+[webpack](http://webpack.github.io/),
+[BrowserSync](http://www.browsersync.io/),
+[Microsoft's Visual Studio Code](https://github.com/microsoft/vscode),
+and [many others](https://www.npmjs.org/browse/depended/chokidar/).
+It has proven itself in production environments.
+
+## How?
+Chokidar does still rely on the Node.js core `fs` module, but when using
+`fs.watch` and `fs.watchFile` for watching, it normalizes the events it
+receives, often checking for truth by getting file stats and/or dir contents.
+
+On Mac OS X, chokidar by default uses a native extension exposing the Darwin
+`FSEvents` API. This provides very efficient recursive watching compared with
+implementations like `kqueue` available on most \*nix platforms. Chokidar still
+does have to do some work to normalize the events received that way as well.
+
+On other platforms, the `fs.watch`-based implementation is the default, which
+avoids polling and keeps CPU usage down. Be advised that chokidar will initiate
+watchers recursively for everything within scope of the paths that have been
+specified, so be judicious about not wasting system resources by watching much
+more than needed.
+
+## Getting started
+Install with npm:
+
+    npm install chokidar --save
+
+Then `require` and use it in your code:
+
+```javascript
+var chokidar = require('chokidar');
+
+// One-liner for current directory, ignores .dotfiles
+chokidar.watch('.', {ignored: /[\/\\]\./}).on('all', (event, path) => {
+  console.log(event, path);
+});
+```
+
+```javascript
+// Example of a more typical implementation structure:
+
+// Initialize watcher.
+var watcher = chokidar.watch('file, dir, glob, or array', {
+  ignored: /[\/\\]\./,
+  persistent: true
+});
+
+// Something to use when events are received.
+var log = console.log.bind(console);
+// Add event listeners.
+watcher
+  .on('add', path => log(`File ${path} has been added`))
+  .on('change', path => log(`File ${path} has been changed`))
+  .on('unlink', path => log(`File ${path} has been removed`));
+
+// More possible events.
+watcher
+  .on('addDir', path => log(`Directory ${path} has been added`))
+  .on('unlinkDir', path => log(`Directory ${path} has been removed`))
+  .on('error', error => log(`Watcher error: ${error}`))
+  .on('ready', () => log('Initial scan complete. Ready for changes'))
+  .on('raw', (event, path, details) => {
+    log('Raw event info:', event, path, details);
+  });
+
+// 'add', 'addDir' and 'change' events also receive stat() results as second
+// argument when available: http://nodejs.org/api/fs.html#fs_class_fs_stats
+watcher.on('change', (path, stats) => {
+  if (stats) console.log(`File ${path} changed size to ${stats.size}`);
+});
+
+// Watch new files.
+watcher.add('new-file');
+watcher.add(['new-file-2', 'new-file-3', '**/other-file*']);
+
+// Get list of actual paths being watched on the filesystem
+var watchedPaths = watcher.getWatched();
+
+// Un-watch some files.
+watcher.unwatch('new-file*');
+
+// Stop watching.
+watcher.close();
+
+// Full list of options. See below for descriptions. (do not use this example)
+chokidar.watch('file', {
+  persistent: true,
+
+  ignored: '*.txt',
+  ignoreInitial: false,
+  followSymlinks: true,
+  cwd: '.',
+
+  usePolling: true,
+  interval: 100,
+  binaryInterval: 300,
+  alwaysStat: false,
+  depth: 99,
+  awaitWriteFinish: {
+    stabilityThreshold: 2000,
+    pollInterval: 100
+  },
+
+  ignorePermissionErrors: false,
+  atomic: true // or a custom 'atomicity delay', in milliseconds (default 100)
+});
+
+```
+
+## API
+
+`chokidar.watch(paths, [options])`
+
+* `paths` (string or array of strings). Paths to files, dirs to be watched
+recursively, or glob patterns.
+* `options` (object) Options object as defined below:
+
+#### Persistence
+
+* `persistent` (default: `true`). Indicates whether the process
+should continue to run as long as files are being watched. If set to
+`false` when using `fsevents` to watch, no more events will be emitted
+after `ready`, even if the process continues to run.
+
+#### Path filtering
+
+* `ignored` ([anymatch](https://github.com/es128/anymatch)-compatible 
definition)
+Defines files/paths to be ignored. The whole relative or absolute path is
+tested, not just filename. If a function with two arguments is provided, it
+gets called twice per path - once with a single argument (the path), second
+time with two arguments (the path and the
+[`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats)
+object of that path).
+* `ignoreInitial` (default: `false`). If set to `false` then `add`/`addDir` 
events are also emitted for matching paths while
+instantiating the watching as chokidar discovers these file paths (before the 
`ready` event).
+* `followSymlinks` (default: `true`). When `false`, only the
+symlinks themselves will be watched for changes instead of following
+the link references and bubbling events through the link's path.
+* `cwd` (no default). The base directory from which watch `paths` are to be
+derived. Paths emitted with events will be relative to this.
+
+#### Performance
+
+* `usePolling` (default: `false`).
+Whether to use fs.watchFile (backed by polling), or fs.watch. If polling
+leads to high CPU utilization, consider setting this to `false`. It is
+typically necessary to **set this to `true` to successfully watch files over
+a network**, and it may be necessary to successfully watch files in other
+non-standard situations. Setting to `true` explicitly on OS X overrides the
+`useFsEvents` default. You may also set the CHOKIDAR_USEPOLLING env variable
+to true (1) or false (0) in order to override this option.
+* _Polling-specific settings_ (effective when `usePolling: true`)
+  * `interval` (default: `100`). Interval of file system polling.
+  * `binaryInterval` (default: `300`). Interval of file system
+  polling for binary files.
+  ([see list of binary 
extensions](https://github.com/sindresorhus/binary-extensions/blob/master/binary-extensions.json))
+* `useFsEvents` (default: `true` on OS X). Whether to use the
+`fsevents` watching interface if available. When set to `true` explicitly
+and `fsevents` is available this supercedes the `usePolling` setting. When
+set to `false` on OS X, `usePolling: true` becomes the default.
+* `alwaysStat` (default: `false`). If relying upon the
+[`fs.Stats`](http://nodejs.org/api/fs.html#fs_class_fs_stats)
+object that may get passed with `add`, `addDir`, and `change` events, set
+this to `true` to ensure it is provided even in cases where it wasn't
+already available from the underlying watch events.
+* `depth` (default: `undefined`). If set, limits how many levels of
+subdirectories will be traversed.
+* `awaitWriteFinish` (default: `false`).
+By default, the `add` event will fire when a file first appear on disk, before
+the entire file has been written. Furthermore, in some cases some `change`
+events will be emitted while the file is being written. In some cases,
+especially when watching for large files there will be a need to wait for the
+write operation to finish before responding to a file creation or modification.
+Setting `awaitWriteFinish` to `true` (or a truthy value) will poll file size,
+holding its `add` and `change` events until the size does not change for a
+configurable amount of time. The appropriate duration setting is heavily
+dependent on the OS and hardware. For accurate detection this parameter should
+be relatively high, making file watching much less responsive.
+Use with caution.
+  * *`options.awaitWriteFinish` can be set to an object in order to adjust
+  timing params:*
+  * `awaitWriteFinish.stabilityThreshold` (default: 2000). Amount of time in
+  milliseconds for a file size to remain constant before emitting its event.
+  * `awaitWriteFinish.pollInterval` (default: 100). File size polling interval.
+
+#### Errors
+* `ignorePermissionErrors` (default: `false`). Indicates whether to watch files
+that don't have read permissions if possible. If watching fails due to `EPERM`
+or `EACCES` with this set to `true`, the errors will be suppressed silently.
+* `atomic` (default: `true` if `useFsEvents` and `usePolling` are `false`).
+Automatically filters out artifacts that occur when using editors that use
+"atomic writes" instead of writing directly to the source file. If a file is
+re-added within 100 ms of being deleted, Chokidar emits a `change` event
+rather than `unlink` then `add`. If the default of 100 ms does not work well
+for you, you can override it by setting `atomic` to a custom value, in
+milliseconds.
+
+### Methods & Events
+
+`chokidar.watch()` produces an instance of `FSWatcher`. Methods of `FSWatcher`:
+
+* `.add(path / paths)`: Add files, directories, or glob patterns for tracking.
+Takes an array of strings or just one string.
+* `.on(event, callback)`: Listen for an FS event.
+Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `ready`,
+`raw`, `error`.
+Additionally `all` is available which gets emitted with the underlying event
+name and path for every event other than `ready`, `raw`, and `error`.
+* `.unwatch(path / paths)`: Stop watching files, directories, or glob patterns.
+Takes an array of strings or just one string.
+* `.close()`: Removes all listeners from watched files.
+* `.getWatched()`: Returns an object representing all the paths on the file
+system being watched by this `FSWatcher` instance. The object's keys are all 
the
+directories (using absolute paths unless the `cwd` option was used), and the
+values are arrays of the names of the items contained in each directory.
+
+## CLI
+
+If you need a CLI interface for your file watching, check out
+[chokidar-cli](https://github.com/kimmobrunfeldt/chokidar-cli), allowing you to
+execute a command on each change, or get a stdio stream of change events.
+
+## Install Troubleshooting
+
+* `npm WARN optional dep failed, continuing fsevents@n.n.n`
+  * This message is normal part of how `npm` handles optional dependencies and 
is
+    not indicative of a problem. Even if accompanied by other related error 
messages,
+    Chokidar should function properly.
+
+* `ERR! stack Error: Python executable "python" is v3.4.1, which is not 
supported by gyp.`
+  * You should be able to resolve this by installing python 2.7 and running:
+    `npm config set python python2.7`
+
+* `gyp ERR! stack Error: not found: make`
+  * On Mac, install the XCode command-line tools
+
+## License
+
+The MIT License (MIT)
+
+Copyright (c) 2016 Paul Miller (http://paulmillr.com) & Elan Shanker
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to 
deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

Reply via email to