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
JavaScript
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;
}
;