siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
382 lines (262 loc) • 12.4 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js">/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
Class('Ariadne.QueryFinder.Maze', {
does : [
Siesta.Util.Role.CanGetType
],
has : {
finder : { required : true },
nodes : Joose.I.Array,
segmentsIdGen : 1,
encounteredMandatoryId : false
},
methods : {
getRoot : function () {
return this.nodes[ this.nodes.length - 1 ]
},
doQuery : function (query, rootNode) {
var queryCache = rootNode.queryCache
// TUNE
// if (queryCache[ query ]) console.log("CACHE HIT!")
if (queryCache[ query ]) return queryCache[ query ]
// // TUNE
// QUERYCOUNTER = window.QUERYCOUNTER || 0
// QUERYCOUNTER++
// console.log("QUERY from:", rootNode.index, " query: ", query)
return queryCache[ query ] = this.finder.doQuery(query, rootNode.el)
},
buildVertices : function (target, root) {
// console.time('buildVertices')
var nodes = this.nodes = []
var currentEl = target
var prevNode
var finder = this.finder
var identifiers = finder.identifiers
var directChildIdentifier = finder.directChildIdentifier
while (currentEl) {
var node = new Ariadne.QueryFinder.Node({
finder : finder,
maze : this,
el : currentEl,
index : nodes.length
})
if (prevNode) {
prevNode.upNode = node
node.downNode = prevNode
if (directChildIdentifier) {
var segment = directChildIdentifier.identify(prevNode.el, root, this)
if (segment) {
segment = this.normalizeIdentifierResult(segment)[ 0 ]
var directChildUpEdge = new Ariadne.QueryFinder.Edge({
finder : finder,
maze : this,
isDirectChild : true,
fromNode : prevNode,
toNode : node
})
prevNode.directChildUpEdge = directChildUpEdge
prevNode.adoptSegment(segment)
directChildUpEdge.consumeVariant([ segment ], finder.combineSegments([ segment ], true))
}
}
}
prevNode = node
nodes.push(node)
// exit a bit earlier for root node - segments won't be set for it
if (currentEl == root) break
var segments = []
for (var j = 0; j < identifiers.length; j++) {
var compSegments = identifiers[ j ].identify(currentEl, root, this)
if (compSegments) {
segments.push.apply(segments, this.normalizeIdentifierResult(compSegments))
}
}
node.setSegments(segments)
currentEl = finder.getParent(currentEl)
}
nodes[ 0 ].weightOptions = [ { weight : 0, minFromEdgesById : {} } ]
nodes[ 0 ].weightOptionsByWeight[ 0 ] = nodes[ 0 ].weightOptions[ 0 ]
// console.timeEnd('buildVertices')
},
normalizeIdentifierResult : function (segments) {
var me = this
if (this.typeOf(segments) != 'Array') segments = [ segments ]
return segments.map(function (segment) {
if (me.typeOf(segment) == 'String')
return {
query : segment,
weight : 1000
}
else {
if (!segment.hasOwnProperty('weight')) segment.weight = 1000
return segment
}
})
},
computePath : function (prevUpdatedNodes) {
var nodes = this.nodes
var root = this.getRoot()
var iteration = 0
if (!prevUpdatedNodes) {
prevUpdatedNodes = {}
prevUpdatedNodes[ nodes[ 0 ].id ] = nodes[ 0 ]
}
do {
iteration++
var updatedNodes = {}
var somethingUpdated = false
for (var id in prevUpdatedNodes) {
var fromNode = prevUpdatedNodes[ id ]
if (fromNode.weightOptions.length === 0) continue
for (var toIndex in fromNode.edgesToByIndex) {
var edge = fromNode.edgesToByIndex[ toIndex ]
var toNode = edge.toNode
if (edge.relax()) {
updatedNodes[ toNode.id ] = toNode
somethingUpdated = true
}
}
var directChildUpEdge = fromNode.directChildUpEdge
if (directChildUpEdge && directChildUpEdge.relax()) {
var toNode = directChildUpEdge.toNode
updatedNodes[ toNode.id ] = toNode
somethingUpdated = true
}
}
prevUpdatedNodes = updatedNodes
} while (somethingUpdated && iteration < nodes.length)
},
buildEdges : function () {
// console.time('buildEdges')
var recognizers = this.finder.recognizers
for (var j = 0; j < recognizers.length; j++) {
recognizers[ j ].recognize(this)
}
var nodes = this.nodes
for (var i = 0; i < nodes.length - 1; i++) {
// early exit, as soon as we found identifying edge
// by definition, we aren't interested in edges above it
if (nodes[ i ].buildEdges3(1) === false) break
nodes[ i ].buildEdges3(2)
nodes[ i ].buildEdges3(3)
}
// console.timeEnd('buildEdges')
},
expandSimplePaths : function (paths) {
var nodes = this.nodes
var pathsHashes = {}
var pathVariants = []
for (var i = 0; i < paths.length; i++) {
var path = paths[ i ].split('_')
var variants = []
var prevIndex
for (var j = path.length - 1; j > 1; j--) {
var isDirectChildEdge = path[ j ][ 0 ] == '+'
var onlyWithOwnIndex = j < path.length - 1 && path[ j + 1 ][ 0 ] == '+'
var fromNode = nodes[ Number(path[ j ]) ]
var edge = isDirectChildEdge ? fromNode.directChildUpEdge : fromNode.edgesToByIndex[ Number(path[ j - 1 ]) ]
variants = fromNode.extendPathVariants(variants, edge.segmentVariants, onlyWithOwnIndex ? fromNode.index : null)
}
for (var k = 0; k < variants.length; k++) {
var pathVariant = variants[ k ]
if (!pathsHashes[ pathVariant.hash ]) {
pathVariants.push(pathVariant)
pathsHashes[ pathVariant.hash ] = true
}
}
}
pathVariants.sort(function (a, b) { return a.weight - b.weight })
return pathVariants
},
findAllPaths : function (options) {
// console.time('computePath')
this.computePath()
// console.timeEnd('computePath')
var me = this
var root = this.getRoot()
// console.time('collectAllPaths')
var simplePathsCollection = root.collectFullSimplePathCollection()
// console.timeEnd('collectAllPaths')
// console.log("Simple paths: ", simplePathsCollection)
for (var i = 0; i < simplePathsCollection.length; i++) {
var simplePaths = simplePathsCollection[ i ].simplePaths
var queryInfos = this.verifySimplePathGroup(simplePaths, options)
if (queryInfos.length) {
if (options && options.detailed) {
return queryInfos
} else
return queryInfos.map(function (queryInfo) { return queryInfo.query })
}
}
return []
},
verifySimplePathGroup : function (simplePaths, options) {
var root = this.getRoot()
var finder = this.finder
// console.time('expandAllPaths')
var pathVariants = this.expandSimplePaths(simplePaths)
// console.timeEnd('expandAllPaths')
// console.time('verifyAllPaths')
var queryInfos = []
var targetEl = this.nodes[ 0 ].el
var rootEl = root.el
for (var i = 0; i < pathVariants.length; i++) {
var variant = pathVariants[ i ]
var segments = []
for (var id in variant.segmentsById) segments.push(variant.segmentsById[ id ])
// query can potentially not be constructed because of clash between leading segments
var queryInfo = finder.combineSegments(segments)
if (queryInfo) {
if (finder.verifyQuery(queryInfo.query, targetEl, rootEl)) {
queryInfos.push(queryInfo)
if (options && options.singleOnly) return queryInfos
} else {
// console.log("WRONG QUERY: " + queryInfo.query)
}
}
}
// console.timeEnd('verifyAllPaths')
return queryInfos
},
// DEBUG METHOD
countEdges : function (log) {
var counter = 0
this.nodes.forEach(function (node) {
for (var index in node.edgesToByIndex) {
var edge = node.edgesToByIndex[ index ]
counter += edge.segmentVariants.length
if (log) {
edge.segmentVariants.forEach(function (variant) {
console.log("Edge from: " + edge.fromNode.index + "[" + edge.fromNode.el.tagName + "] to " + edge.toNode.index + "[" + edge.toNode.el.tagName + "] query: " + edge.finder.combineSegments(variant.variant).query)
})
}
}
})
return counter
}
}
});
</pre>
</body>
</html>