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.