chowdown
Version:
A JavaScript library that allows for the quick transformation of DOM documents into useful formats.
222 lines (195 loc) • 8.58 kB
JavaScript
;
function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
var Promise = require('bluebird');
var _require = require('lodash'),
assignIn = _require.assignIn,
castArray = _require.castArray,
identity = _require.identity,
flow = _require.flow,
extendWith = _require.extendWith,
first = _require.first,
isPlainObject = _require.isPlainObject,
isFunction = _require.isFunction;
/**
* The ultimate class representing a query.
*
* When executed, this query will find attempt to find a value inside a given
* document and return a promise resolving to it.
*
* @class Query
*/
var Query =
/*#__PURE__*/
function () {
/**
* Constructs an query given a document selector and an object
* containing additional configuration options.
*
* @param {string} selector A selector for this query's value in a document.
* @param {object} [options] An object containing additional configuration options.
* @param {any} [options.default] The default valuye to return if no value is found.
* @param {boolean} [options.throwOnMissing=false] Whether or not to throw an error if no value is found.
* @param {function[]} [options.format=[]] The functions used to format the value.
*/
function Query(selector) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, Query);
options.selector = selector;
this.configure(options);
}
/**
* Configures this query given an object of configuration options.
*
* By default, throwOnMissing will be set to false and no format functions will
* be set.
*
* @param {options} options The object of configuration options.
* @param {string} options.selector A selector for this query's value in a document.
* @param {any} [options.default] The default valuye to return if no value is found.
* @param {boolean} [options.throwOnMissing=false] Whether or not to throw an error if no value is found.
* @param {function[]} [options.format=[]] The functions used to format the value.
*/
_createClass(Query, [{
key: "configure",
value: function configure(options) {
this.options = assignIn(this.options, options);
this.options.format = castArray(this.options.format || []);
if (this.options.throwOnMissing === undefined) this.options.throwOnMissing = false;
}
/**
* Retrieves a raw value from the document using the query's selector.
*
* @param {Document} document The document to retrieve the value from.
* @return {any} The retrieved value.
*/
}, {
key: "find",
value: function find(document) {
return document.value(this.options.selector);
}
/**
* Determines if the query's default value should be used instead
* of the value that was retrieved from the document.
*
* If no value was retrieved and the throwOnMissing
* options is set to true, an error will be thrown.
*
* @param {any} value The value retreived from the document.
* @param {Document} document The document the value was retrieved from.
* @return {any} The query's default value or the value retrieved from the document.
*/
}, {
key: "default",
value: function _default(value, document) {
if (value !== undefined) return value;
if (this.options.throwOnMissing) throw new Error();
return this.options["default"];
}
/**
* "Builds" the retrieved value such that it is ready for formatting.
*
* @param {any} value The query's value retrieved from the document.
* @param {Document} document The document the value was retrieved from.
* @return {any} The built value.
*/
}, {
key: "build",
value: function build(value, document) {
return value;
}
/**
* Formats the built value by feeding it through the query's format functions.
*
* @param {any} value The query's value.
* @param {Document} document The document the value was retrieved from.
* @return {any} The formatted value.
*/
}, {
key: "format",
value: function format(value, document) {
return flow(this.options.format)(value);
}
/**
* Executes this query on the given document.
*
* @param {Document} The document to execute this query on.
* @return {any} A promise that resolves to the value of this query.
*/
}, {
key: "on",
value: function on(document) {
var _this = this;
return Promise.resolve(document).then(function (document) {
return _this.find(document);
}).then(function (value) {
return _this["default"](value, document);
}).then(function (value) {
return _this.build(value, document);
}).then(function (value) {
return _this.format(value, document);
});
}
}]);
return Query;
}();
module.exports = Query;
/**
* An object that maps query subclass names to their respective classes.
*
* @type {object}
*/
var children = {
raw: require('./raw'),
string: require('./string'),
number: require('./number'),
object: require('./object'),
collection: require('./collection'),
context: require('./context'),
callback: require('./callback'),
follow: require('./follow'),
uri: require('./uri'),
regex: require('./regex'),
paginate: require('./paginate')
};
/**
* A function that when passed a "selector", will determine what
* type of query to create and create it.
*
* If an existing Query is passed, then it will be returned.
*
* Accepts a default factory function (create) that will be called
* if no matching type is found.
*
* @param {any} selector The selector for create a query for.
* @param {function} create A default factory function.
* @return {Query} The constructed query.
*/
Query.factory = function (selector) {
var create = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Query.factory.string;
if (isPlainObject(selector)) create = Query.factory.object;
if (isFunction(selector)) create = Query.factory.callback;
return create(selector);
};
/**
* Creates a method on the factory function for each query subclass.
*
* Each method takes an arbitrary number of arguments that are
* passed through to the respective query constructor.
*
* If the first argument is already an Query, then it's
* is returned and the consructor is not exectuted.
*/
extendWith(Query.factory, children, function (_, type) {
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (first(args) instanceof Query) return first(args);
return _construct(type, args);
};
});