UNPKG

simmerjs

Version:

A pure Javascript reverse CSS selector engine which calculates a DOM element's unique CSS selector on the current page.

153 lines (121 loc) 6.21 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; exports.default = createSimmer; var _queryEngine = require('./queryEngine'); var _queryEngine2 = _interopRequireDefault(_queryEngine); var _methods = require('./methods'); var _methods2 = _interopRequireDefault(_methods); var _validateSelector = require('./validateSelector'); var _validateSelector2 = _interopRequireDefault(_validateSelector); var _convertSelectorStateIntoCSSSelector = require('./convertSelectorStateIntoCSSSelector'); var _convertSelectorStateIntoCSSSelector2 = _interopRequireDefault(_convertSelectorStateIntoCSSSelector); var _parser = require('./parser'); var _parser2 = _interopRequireDefault(_parser); var _stackHierarchy = require('./stackHierarchy'); var _stackHierarchy2 = _interopRequireDefault(_stackHierarchy); var _configuration = require('./configuration'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function createSimmer() { var windowScope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window; var customConfig = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var customQuery = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var config = (0, _configuration.configure)(customConfig); var query = customQuery || (0, _queryEngine2.default)(windowScope, config.queryEngine /** * Handle errors in accordance with what is specified in the configuration * @param {object/string} ex. The exception object or message * @param {object} element. The element Simmer was asked to process */ );function onError(ex, element) { // handle error if (config.errorHandling === true) { throw ex; } if (typeof config.errorHandling === 'function') { config.errorHandling(ex, element); } } // Initialize the Simmer object and set it over the reference on the window /** * The main Simmer action - parses an element on the page to produce a CSS selector for it. * This function will be returned into the global Simmer object. * @param {object} element. A DOM element you wish to create a selector for. * @example <code><pre> var cssSelectorForDonJulio = Simmer(document.getElementByID('DonJulio')); </pre></code> */ var simmer = function simmer(element) { if (!element) { // handle error onError.call(simmer, new Error('Simmer: No element was specified for parsing.'), element); return false; } // The parser cycles through a set of parsing methods specified in an order optimal // for creating as specific as possible a selector var parser = new _parser2.default(_methods2.default); // get the element's ancestors var hierarchy = (0, _stackHierarchy2.default)((0, _queryEngine.wrap)(element), config.depth // initialize the state of the selector );var selectorState = { // the stack is used to build a layer of selectors, each layer coresponding to a specific element in the heirarchy // for each level we create a private stack of properties, so that we can then merge them // comfortably and allow all methods to see the level at which existing properties have been set stack: Array(hierarchy.length).fill().map(function () { return []; }), // follow the current specificity level of the selector - the higher the better specificity: 0 }; var validator = (0, _validateSelector2.default)(element, config, query, onError // cycle through the available parsing methods and while we still have yet to find the requested element's one-to-one selector // we keep calling the methods until we are either satisfied or run out of methods );while (!parser.finished() && !selectorState.verified) { try { selectorState = parser.next(hierarchy, selectorState, validator, config, query // if we have reached a satisfactory level of specificity, try the selector, perhaps we have found our selector? );if (selectorState.specificity >= config.specificityThreshold && !selectorState.verified) { selectorState.verified = validator(selectorState); } } catch (ex) { // handle error onError.call(simmer, ex, element); } } // if we were not able to produce a one-to-one selector, return false if (selectorState.verified === undefined || selectorState.specificity < config.specificityThreshold) { // if it is undefined then verfication has never been run! // try and verify, and if verification fails - return false // if it is false and the specificity is too low to actually try and find the element in the first place, then we may simply have not run // an up to date verification - try again selectorState.verified = validator(selectorState); } if (!selectorState.verified) { return false; } if (selectorState.verificationDepth) { return (0, _convertSelectorStateIntoCSSSelector2.default)(selectorState, selectorState.verificationDepth); } return (0, _convertSelectorStateIntoCSSSelector2.default)(selectorState); }; /** * Get/Set the configuration for the Simmer object * @param config (Object) A configuration object with any of the properties tweeked (none/depth/minimumSpecificity) * @example <code><pre> configuration({ depth: 3 }); </pre></code> */ simmer.configure = function () { var configValues = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var scope = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : windowScope; var newConfig = (0, _configuration.configure)(_extends({}, config, configValues)); return createSimmer(scope, newConfig, (0, _queryEngine2.default)(scope, newConfig.queryEngine)); }; return simmer; }