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