@@ -480,4 +480,236 @@ LocatorBuilders.add('xpath:idRelative', function(e) {
480480        current  =  current . parentNode ; 
481481    } 
482482    return  null ; 
483- } ) ; 
483+ } ) ; 
484+ 
485+ // The following code is fetched from https://github.com/firebug/firebug which is BSD licensed 
486+ // A 3rd party tool to build exact xpath /html/body/div/... 
487+ // Have not implemented this into locator_builder yet 
488+ var  Xpath  =  { } ; 
489+ Xpath . getElementXPath  =  function ( element ) 
490+ { 
491+     if  ( element  &&  element . id ) 
492+         return  '//*[@id="'  +  element . id  +  '"]' ; 
493+     else 
494+         return  Xpath . getElementTreeXPath ( element ) ; 
495+ } ; 
496+ 
497+ Xpath . getElementTreeXPath  =  function ( element ) 
498+ { 
499+     var  paths  =  [ ] ; 
500+ 
501+     // Use nodeName (instead of localName) so namespace prefix is included (if any). 
502+     for  ( ;  element  &&  element . nodeType  ==  Node . ELEMENT_NODE ;  element  =  element . parentNode ) 
503+     { 
504+         var  index  =  0 ; 
505+         var  hasFollowingSiblings  =  false ; 
506+         for  ( var  sibling  =  element . previousSibling ;  sibling ;  sibling  =  sibling . previousSibling ) 
507+         { 
508+             // Ignore document type declaration. 
509+             if  ( sibling . nodeType  ==  Node . DOCUMENT_TYPE_NODE ) 
510+                 continue ; 
511+ 
512+             if  ( sibling . nodeName  ==  element . nodeName ) 
513+                 ++ index ; 
514+         } 
515+ 
516+         for  ( var  sibling  =  element . nextSibling ;  sibling  &&  ! hasFollowingSiblings ; 
517+             sibling  =  sibling . nextSibling ) 
518+         { 
519+             if  ( sibling . nodeName  ==  element . nodeName ) 
520+                 hasFollowingSiblings  =  true ; 
521+         } 
522+ 
523+         var  tagName  =  ( element . prefix  ? element . prefix  +  ":"  : "" )  +  element . localName ; 
524+         var  pathIndex  =  ( index  ||  hasFollowingSiblings  ? "["  +  ( index  +  1 )  +  "]"  : "" ) ; 
525+         paths . splice ( 0 ,  0 ,  tagName  +  pathIndex ) ; 
526+     } 
527+ 
528+     return  paths . length  ? "/"  +  paths . join ( "/" )  : null ; 
529+ } ; 
530+ 
531+ Xpath . cssToXPath  =  function ( rule ) 
532+ { 
533+     var  regElement  =  / ^ ( [ # . ] ? ) ( [ a - z 0 - 9 \\ * _ - ] * ) ( ( \| ) ( [ a - z 0 - 9 \\ * _ - ] * ) ) ? / i; 
534+     var  regAttr1  =  / ^ \[ ( [ ^ \] ] * ) \] / i; 
535+     var  regAttr2  =  / ^ \[ \s * ( [ ^ ~ = \s ] + ) \s * ( ~ ? = ) \s * " ( [ ^ " ] + ) " \s * \] / i; 
536+     var  regPseudo  =  / ^ : ( [ a - z _ - ] ) + / i; 
537+     var  regCombinator  =  / ^ ( \s * [ > + \s ] ) ? / i; 
538+     var  regComma  =  / ^ \s * , / i; 
539+ 
540+     var  index  =  1 ; 
541+     var  parts  =  [ "//" ,  "*" ] ; 
542+     var  lastRule  =  null ; 
543+ 
544+     while  ( rule . length  &&  rule  !=  lastRule ) 
545+     { 
546+         lastRule  =  rule ; 
547+ 
548+         // Trim leading whitespace 
549+         rule  =  Str . trim ( rule ) ; 
550+         if  ( ! rule . length ) 
551+             break ; 
552+ 
553+         // Match the element identifier 
554+         var  m  =  regElement . exec ( rule ) ; 
555+         if  ( m ) 
556+         { 
557+             if  ( ! m [ 1 ] ) 
558+             { 
559+                 // XXXjoe Namespace ignored for now 
560+                 if  ( m [ 5 ] ) 
561+                     parts [ index ]  =  m [ 5 ] ; 
562+                 else 
563+                     parts [ index ]  =  m [ 2 ] ; 
564+             } 
565+             else  if  ( m [ 1 ]  ==  '#' ) 
566+                 parts . push ( "[@id='"  +  m [ 2 ]  +  "']" ) ; 
567+             else  if  ( m [ 1 ]  ==  '.' ) 
568+                 parts . push ( "[contains(concat(' ',normalize-space(@class),' '), ' "  +  m [ 2 ]  +  " ')]" ) ; 
569+ 
570+             rule  =  rule . substr ( m [ 0 ] . length ) ; 
571+         } 
572+ 
573+         // Match attribute selectors 
574+         m  =  regAttr2 . exec ( rule ) ; 
575+         if  ( m ) 
576+         { 
577+             if  ( m [ 2 ]  ==  "~=" ) 
578+                 parts . push ( "[contains(@"  +  m [ 1 ]  +  ", '"  +  m [ 3 ]  +  "')]" ) ; 
579+             else 
580+                 parts . push ( "[@"  +  m [ 1 ]  +  "='"  +  m [ 3 ]  +  "']" ) ; 
581+ 
582+             rule  =  rule . substr ( m [ 0 ] . length ) ; 
583+         } 
584+         else 
585+         { 
586+             m  =  regAttr1 . exec ( rule ) ; 
587+             if  ( m ) 
588+             { 
589+                 parts . push ( "[@"  +  m [ 1 ]  +  "]" ) ; 
590+                 rule  =  rule . substr ( m [ 0 ] . length ) ; 
591+             } 
592+         } 
593+ 
594+         // Skip over pseudo-classes and pseudo-elements, which are of no use to us 
595+         m  =  regPseudo . exec ( rule ) ; 
596+         while  ( m ) 
597+         { 
598+             rule  =  rule . substr ( m [ 0 ] . length ) ; 
599+             m  =  regPseudo . exec ( rule ) ; 
600+         } 
601+ 
602+         // Match combinators 
603+         m  =  regCombinator . exec ( rule ) ; 
604+         if  ( m  &&  m [ 0 ] . length ) 
605+         { 
606+             if  ( m [ 0 ] . indexOf ( ">" )  !=  - 1 ) 
607+                 parts . push ( "/" ) ; 
608+             else  if  ( m [ 0 ] . indexOf ( "+" )  !=  - 1 ) 
609+                 parts . push ( "/following-sibling::" ) ; 
610+             else 
611+                 parts . push ( "//" ) ; 
612+ 
613+             index  =  parts . length ; 
614+             parts . push ( "*" ) ; 
615+             rule  =  rule . substr ( m [ 0 ] . length ) ; 
616+         } 
617+ 
618+         m  =  regComma . exec ( rule ) ; 
619+         if  ( m ) 
620+         { 
621+             parts . push ( " | " ,  "//" ,  "*" ) ; 
622+             index  =  parts . length - 1 ; 
623+             rule  =  rule . substr ( m [ 0 ] . length ) ; 
624+         } 
625+     } 
626+ 
627+     var  xpath  =  parts . join ( "" ) ; 
628+     return  xpath ; 
629+ } ; 
630+ 
631+ Xpath . getElementsBySelector  =  function ( doc ,  css ) 
632+ { 
633+     var  xpath  =  Xpath . cssToXPath ( css ) ; 
634+     return  Xpath . getElementsByXPath ( doc ,  xpath ) ; 
635+ } ; 
636+ 
637+ Xpath . getElementsByXPath  =  function ( doc ,  xpath ) 
638+ { 
639+     try 
640+     { 
641+         return  Xpath . evaluateXPath ( doc ,  xpath ) ; 
642+     } 
643+     catch ( ex ) 
644+     { 
645+         return  [ ] ; 
646+     } 
647+ } ; 
648+ 
649+ /** 
650+  * Evaluates an XPath expression. 
651+  * 
652+  * @param  {Document } doc 
653+  * @param  {String } xpath The XPath expression. 
654+  * @param  {Node } contextNode The context node. 
655+  * @param  {int } resultType 
656+  * 
657+  * @returns  {* } The result of the XPath expression, depending on resultType :<br> <ul> 
658+  *          <li>if it is XPathResult.NUMBER_TYPE, then it returns a Number</li> 
659+  *          <li>if it is XPathResult.STRING_TYPE, then it returns a String</li> 
660+  *          <li>if it is XPathResult.BOOLEAN_TYPE, then it returns a boolean</li> 
661+  *          <li>if it is XPathResult.UNORDERED_NODE_ITERATOR_TYPE 
662+  *              or XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, then it returns an array of nodes</li> 
663+  *          <li>if it is XPathResult.ORDERED_NODE_SNAPSHOT_TYPE 
664+  *              or XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, then it returns an array of nodes</li> 
665+  *          <li>if it is XPathResult.ANY_UNORDERED_NODE_TYPE 
666+  *              or XPathResult.FIRST_ORDERED_NODE_TYPE, then it returns a single node</li> 
667+  *          </ul> 
668+  */ 
669+ Xpath . evaluateXPath  =  function ( doc ,  xpath ,  contextNode ,  resultType ) 
670+ { 
671+     if  ( contextNode  ===  undefined ) 
672+         contextNode  =  doc ; 
673+ 
674+     if  ( resultType  ===  undefined ) 
675+         resultType  =  XPathResult . ANY_TYPE ; 
676+ 
677+     var  result  =  doc . evaluate ( xpath ,  contextNode ,  null ,  resultType ,  null ) ; 
678+ 
679+     switch  ( result . resultType ) 
680+     { 
681+         case  XPathResult . NUMBER_TYPE :
682+             return  result . numberValue ; 
683+ 
684+         case  XPathResult . STRING_TYPE :
685+             return  result . stringValue ; 
686+ 
687+         case  XPathResult . BOOLEAN_TYPE :
688+             return  result . booleanValue ; 
689+ 
690+         case  XPathResult . UNORDERED_NODE_ITERATOR_TYPE :
691+         case  XPathResult . ORDERED_NODE_ITERATOR_TYPE :
692+             var  nodes  =  [ ] ; 
693+             for  ( var  item  =  result . iterateNext ( ) ;  item ;  item  =  result . iterateNext ( ) ) 
694+                 nodes . push ( item ) ; 
695+             return  nodes ; 
696+ 
697+         case  XPathResult . UNORDERED_NODE_SNAPSHOT_TYPE :
698+         case  XPathResult . ORDERED_NODE_SNAPSHOT_TYPE :
699+             var  nodes  =  [ ] ; 
700+             for  ( var  i  =  0 ;  i  <  result . snapshotLength ;  ++ i ) 
701+                 nodes . push ( result . snapshotItem ( i ) ) ; 
702+             return  nodes ; 
703+ 
704+         case  XPathResult . ANY_UNORDERED_NODE_TYPE :
705+         case  XPathResult . FIRST_ORDERED_NODE_TYPE :
706+             return  result . singleNodeValue ; 
707+     } 
708+ } ; 
709+ 
710+ Xpath . getRuleMatchingElements  =  function ( rule ,  doc ) 
711+ { 
712+     var  css  =  rule . selectorText ; 
713+     var  xpath  =  Xpath . cssToXPath ( css ) ; 
714+     return  Xpath . getElementsByXPath ( doc ,  xpath ) ; 
715+ } ; 
0 commit comments