blob: 6394cbd01693653a566d8a78c17581ff3f42809b [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001'use strict'
2
3module.exports = adapterFactory;
4
5function adapterFactory(implementation){
6 ensureImplementation(implementation);
7
8 var adapter = {}
9
10 var baseAdapter = {
11 removeSubsets: function (nodes){
12 return removeSubsets(adapter, nodes);
13 },
14 existsOne: function(test, elems){
15 return existsOne(adapter, test, elems);
16 },
17 getSiblings: function(elem){
18 return getSiblings(adapter, elem);
19 },
20 hasAttrib: function(elem, name){
21 return hasAttrib(adapter, elem, name);
22 },
23 findOne: function(test, arr){
24 return findOne(adapter, test, arr);
25 },
26 findAll: function(test, elems){
27 return findAll(adapter, test, elems)
28 }
29 };
30
31 Object.assign(adapter, baseAdapter, implementation);
32
33 return adapter;
34}
35
36var expectImplemented = [
37 "isTag", "getAttributeValue", "getChildren", "getName", "getParent",
38 "getText"
39];
40
41function ensureImplementation(implementation){
42 if(!implementation) throw new TypeError("Expected implementation")
43
44 var notImplemented = expectImplemented.filter(function(fname){
45 return typeof implementation[fname] !== "function";
46 });
47
48 if(notImplemented.length){
49 var notList = "(" + notImplemented.join(", ") + ")";
50 var message = "Expected functions " + notList + " to be implemented";
51 throw new Error(message);
52 }
53}
54
55function removeSubsets(adapter, nodes){
56 var idx = nodes.length, node, ancestor, replace;
57
58 // Check if each node (or one of its ancestors) is already contained in the
59 // array.
60 while(--idx > -1){
61 node = ancestor = nodes[idx];
62
63 // Temporarily remove the node under consideration
64 nodes[idx] = null;
65 replace = true;
66
67 while(ancestor){
68 if(nodes.indexOf(ancestor) > -1){
69 replace = false;
70 nodes.splice(idx, 1);
71 break;
72 }
73 ancestor = adapter.getParent(ancestor)
74 }
75
76 // If the node has been found to be unique, re-insert it.
77 if(replace){
78 nodes[idx] = node;
79 }
80 }
81
82 return nodes;
83}
84
85function existsOne(adapter, test, elems){
86 return elems.some(function(elem){
87 return adapter.isTag(elem) ?
88 test(elem) || adapter.existsOne(test, adapter.getChildren(elem)) :
89 false;
90 });
91}
92
93function getSiblings(adapter, elem){
94 var parent = adapter.getParent(elem);
95 return parent && adapter.getChildren(parent);
96}
97
98
99function hasAttrib(adapter, elem, name){
100 return adapter.getAttributeValue(elem,name) !== undefined
101}
102
103function findOne(adapter, test, arr){
104 var elem = null;
105
106 for(var i = 0, l = arr.length; i < l && !elem; i++){
107 if(test(arr[i])){
108 elem = arr[i];
109 } else {
110 var childs = adapter.getChildren(arr[i]);
111 if(childs && childs.length > 0){
112 elem = adapter.findOne(test, childs);
113 }
114 }
115 }
116
117 return elem;
118}
119
120function findAll(adapter, test, elems){
121 var result = [];
122
123 for(var i = 0, j = elems.length; i < j; i++){
124 if(!adapter.isTag(elems[i])) continue;
125 if(test(elems[i])) result.push(elems[i]);
126 var childs = adapter.getChildren(elems[i]);
127 if(childs) result = result.concat(adapter.findAll(test, childs));
128 }
129
130 return result;
131}