index.js 2.88 KB
Newer Older
liang ce committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
'use strict'

module.exports = adapterFactory;

function adapterFactory(implementation){
	ensureImplementation(implementation);

	var adapter = {}

	var baseAdapter = {
		removeSubsets: function (nodes){
			return removeSubsets(adapter, nodes);
		},
		existsOne: function(test, elems){
			return existsOne(adapter, test, elems);
		},
		getSiblings: function(elem){
			return getSiblings(adapter, elem);
		},
		hasAttrib: function(elem, name){
			return hasAttrib(adapter, elem, name);
		},
		findOne: function(test, arr){
			return findOne(adapter, test, arr);
		},
		findAll: function(test, elems){
			return findAll(adapter, test, elems)
		}
	};

	Object.assign(adapter, baseAdapter, implementation);

	return adapter;
}

var expectImplemented = [
	"isTag", "getAttributeValue", "getChildren", "getName", "getParent",
	"getText"
];

function ensureImplementation(implementation){
	if(!implementation)	throw new TypeError("Expected implementation")

	var notImplemented = expectImplemented.filter(function(fname){
		return typeof implementation[fname] !== "function";
	});

	if(notImplemented.length){
		var notList = "(" + notImplemented.join(", ") + ")";
		var message = "Expected functions " + notList + " to be implemented";
		throw new Error(message);
	}
}

function removeSubsets(adapter, nodes){
	var idx = nodes.length, node, ancestor, replace;

	// Check if each node (or one of its ancestors) is already contained in the
	// array.
	while(--idx > -1){
		node = ancestor = nodes[idx];

		// Temporarily remove the node under consideration
		nodes[idx] = null;
		replace = true;

		while(ancestor){
			if(nodes.indexOf(ancestor) > -1){
				replace = false;
				nodes.splice(idx, 1);
				break;
			}
			ancestor = adapter.getParent(ancestor)
		}

		// If the node has been found to be unique, re-insert it.
		if(replace){
			nodes[idx] = node;
		}
	}

	return nodes;
}

function existsOne(adapter, test, elems){
	return elems.some(function(elem){
		return adapter.isTag(elem) ?
			test(elem) || adapter.existsOne(test, adapter.getChildren(elem)) :
			false;
	});
}

function getSiblings(adapter, elem){
	var parent = adapter.getParent(elem);
	return parent && adapter.getChildren(parent);
}


function hasAttrib(adapter, elem, name){
	return adapter.getAttributeValue(elem,name) !== undefined
}

function findOne(adapter, test, arr){
	var elem = null;

	for(var i = 0, l = arr.length; i < l && !elem; i++){
		if(test(arr[i])){
			elem = arr[i];
		} else {
			var childs = adapter.getChildren(arr[i]);
			if(childs && childs.length > 0){
				elem = adapter.findOne(test, childs);
			}
		}
	}

	return elem;
}

function findAll(adapter, test, elems){
	var result = [];

	for(var i = 0, j = elems.length; i < j; i++){
		if(!adapter.isTag(elems[i])) continue;
		if(test(elems[i])) result.push(elems[i]);
		var childs = adapter.getChildren(elems[i]);
		if(childs) result = result.concat(adapter.findAll(test, childs));
	}

	return result;
}