DOMPath.js 5.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. export const fullQualifiedSelector=function(node,justSelector){if(node.nodeType()!==Node.ELEMENT_NODE){return node.localName()||node.nodeName().toLowerCase();}
  2. return cssPath(node,justSelector);};export const cssPath=function(node,optimized){if(node.nodeType()!==Node.ELEMENT_NODE){return'';}
  3. const steps=[];let contextNode=node;while(contextNode){const step=_cssPathStep(contextNode,!!optimized,contextNode===node);if(!step){break;}
  4. steps.push(step);if(step.optimized){break;}
  5. contextNode=contextNode.parentNode;}
  6. steps.reverse();return steps.join(' > ');};export const canGetJSPath=function(node){let wp=node;while(wp){if(wp.ancestorShadowRoot()&&wp.ancestorShadowRoot().shadowRootType()!==SDK.DOMNode.ShadowRootTypes.Open){return false;}
  7. wp=wp.ancestorShadowHost();}
  8. return true;};export const jsPath=function(node,optimized){if(node.nodeType()!==Node.ELEMENT_NODE){return'';}
  9. const path=[];let wp=node;while(wp){path.push(cssPath(wp,optimized));wp=wp.ancestorShadowHost();}
  10. path.reverse();let result='';for(let i=0;i<path.length;++i){const string=JSON.stringify(path[i]);if(i){result+=`.shadowRoot.querySelector(${string})`;}else{result+=`document.querySelector(${string})`;}}
  11. return result;};export const _cssPathStep=function(node,optimized,isTargetNode){if(node.nodeType()!==Node.ELEMENT_NODE){return null;}
  12. const id=node.getAttribute('id');if(optimized){if(id){return new Step(idSelector(id),true);}
  13. const nodeNameLower=node.nodeName().toLowerCase();if(nodeNameLower==='body'||nodeNameLower==='head'||nodeNameLower==='html'){return new Step(node.nodeNameInCorrectCase(),true);}}
  14. const nodeName=node.nodeNameInCorrectCase();if(id){return new Step(nodeName+idSelector(id),true);}
  15. const parent=node.parentNode;if(!parent||parent.nodeType()===Node.DOCUMENT_NODE){return new Step(nodeName,true);}
  16. function prefixedElementClassNames(node){const classAttribute=node.getAttribute('class');if(!classAttribute){return[];}
  17. return classAttribute.split(/\s+/g).filter(Boolean).map(function(name){return'$'+name;});}
  18. function idSelector(id){return'#'+CSS.escape(id);}
  19. const prefixedOwnClassNamesArray=prefixedElementClassNames(node);let needsClassNames=false;let needsNthChild=false;let ownIndex=-1;let elementIndex=-1;const siblings=parent.children();for(let i=0;(ownIndex===-1||!needsNthChild)&&i<siblings.length;++i){const sibling=siblings[i];if(sibling.nodeType()!==Node.ELEMENT_NODE){continue;}
  20. elementIndex+=1;if(sibling===node){ownIndex=elementIndex;continue;}
  21. if(needsNthChild){continue;}
  22. if(sibling.nodeNameInCorrectCase()!==nodeName){continue;}
  23. needsClassNames=true;const ownClassNames=new Set(prefixedOwnClassNamesArray);if(!ownClassNames.size){needsNthChild=true;continue;}
  24. const siblingClassNamesArray=prefixedElementClassNames(sibling);for(let j=0;j<siblingClassNamesArray.length;++j){const siblingClass=siblingClassNamesArray[j];if(!ownClassNames.has(siblingClass)){continue;}
  25. ownClassNames.delete(siblingClass);if(!ownClassNames.size){needsNthChild=true;break;}}}
  26. let result=nodeName;if(isTargetNode&&nodeName.toLowerCase()==='input'&&node.getAttribute('type')&&!node.getAttribute('id')&&!node.getAttribute('class')){result+='[type='+CSS.escape(node.getAttribute('type'))+']';}
  27. if(needsNthChild){result+=':nth-child('+(ownIndex+1)+')';}else if(needsClassNames){for(const prefixedName of prefixedOwnClassNamesArray){result+='.'+CSS.escape(prefixedName.slice(1));}}
  28. return new Step(result,false);};export const xPath=function(node,optimized){if(node.nodeType()===Node.DOCUMENT_NODE){return'/';}
  29. const steps=[];let contextNode=node;while(contextNode){const step=_xPathValue(contextNode,optimized);if(!step){break;}
  30. steps.push(step);if(step.optimized){break;}
  31. contextNode=contextNode.parentNode;}
  32. steps.reverse();return(steps.length&&steps[0].optimized?'':'/')+steps.join('/');};export const _xPathValue=function(node,optimized){let ownValue;const ownIndex=_xPathIndex(node);if(ownIndex===-1){return null;}
  33. switch(node.nodeType()){case Node.ELEMENT_NODE:if(optimized&&node.getAttribute('id')){return new Step('//*[@id="'+node.getAttribute('id')+'"]',true);}
  34. ownValue=node.localName();break;case Node.ATTRIBUTE_NODE:ownValue='@'+node.nodeName();break;case Node.TEXT_NODE:case Node.CDATA_SECTION_NODE:ownValue='text()';break;case Node.PROCESSING_INSTRUCTION_NODE:ownValue='processing-instruction()';break;case Node.COMMENT_NODE:ownValue='comment()';break;case Node.DOCUMENT_NODE:ownValue='';break;default:ownValue='';break;}
  35. if(ownIndex>0){ownValue+='['+ownIndex+']';}
  36. return new Step(ownValue,node.nodeType()===Node.DOCUMENT_NODE);};export const _xPathIndex=function(node){function areNodesSimilar(left,right){if(left===right){return true;}
  37. if(left.nodeType()===Node.ELEMENT_NODE&&right.nodeType()===Node.ELEMENT_NODE){return left.localName()===right.localName();}
  38. if(left.nodeType()===right.nodeType()){return true;}
  39. const leftType=left.nodeType()===Node.CDATA_SECTION_NODE?Node.TEXT_NODE:left.nodeType();const rightType=right.nodeType()===Node.CDATA_SECTION_NODE?Node.TEXT_NODE:right.nodeType();return leftType===rightType;}
  40. const siblings=node.parentNode?node.parentNode.children():null;if(!siblings){return 0;}
  41. let hasSameNamedElements;for(let i=0;i<siblings.length;++i){if(areNodesSimilar(node,siblings[i])&&siblings[i]!==node){hasSameNamedElements=true;break;}}
  42. if(!hasSameNamedElements){return 0;}
  43. let ownIndex=1;for(let i=0;i<siblings.length;++i){if(areNodesSimilar(node,siblings[i])){if(siblings[i]===node){return ownIndex;}
  44. ++ownIndex;}}
  45. return-1;};export class Step{constructor(value,optimized){this.value=value;this.optimized=optimized||false;}
  46. toString(){return this.value;}}
  47. self.Elements=self.Elements||{};Elements=Elements||{};Elements.DOMPath={};Elements.DOMPath.fullQualifiedSelector=fullQualifiedSelector;Elements.DOMPath.cssPath=cssPath;Elements.DOMPath.canGetJSPath=canGetJSPath;Elements.DOMPath.jsPath=jsPath;Elements.DOMPath._cssPathStep=_cssPathStep;Elements.DOMPath.xPath=xPath;Elements.DOMPath._xPathValue=_xPathValue;Elements.DOMPath._xPathIndex=_xPathIndex;Elements.DOMPath.Step=Step;