utilities.html 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Virtual World Framework</title>
  5. <script type="text/javascript" src="qunit.js"></script>
  6. <script type="text/javascript" src="../lib/require.js"></script>
  7. <script type="text/javascript">
  8. require( {
  9. baseUrl: "../lib"
  10. }, [
  11. "domReady",
  12. "vwf/utility",
  13. ], function( ready, utility ) {
  14. // Test utility module functions.
  15. ready( function() {
  16. // == transform ==========================================================================
  17. test( "Identity", function() {
  18. // Transformation function
  19. var transformation = function( object ) {
  20. return object; // no change
  21. };
  22. // Scalars
  23. equal( utility.transform( true, transformation ), true, "bool" );
  24. equal( utility.transform( 1, transformation ), 1, "number" );
  25. equal( utility.transform( "string", transformation ), "string", "string" );
  26. // Objects
  27. var object = {};
  28. equal( utility.transform( object, transformation ), object, "Object" );
  29. var array = [];
  30. equal( utility.transform( array, transformation ), array, "Array" );
  31. } );
  32. test( "Container substitution", function() {
  33. // Transformation function
  34. var transformation = function( object ) {
  35. return typeof object == "object" ? "replaced" : object; // replace Objects and Arrays
  36. };
  37. // Scalars
  38. equal( utility.transform( true, transformation ), true, "bool unchanged" );
  39. equal( utility.transform( 1, transformation ), 1, "number unchanged" );
  40. equal( utility.transform( "string", transformation ), "string", "string unchanged" );
  41. // Objects
  42. var object = {};
  43. equal( utility.transform( object, transformation ), "replaced", "object replaced" );
  44. var array = [];
  45. equal( utility.transform( array, transformation ), "replaced", "array replaced" );
  46. } );
  47. test( "Element substitution", function() {
  48. // Transformation function
  49. var transformation = function( object ) {
  50. return object.replace ? { replaced: true } : object; // replace Objects having a "replace" field
  51. };
  52. // Test elements
  53. var elements = [ { replace: true }, {} ];
  54. // Object container
  55. var object = { zero: elements[0], one: elements[1] };
  56. var transformedObject = utility.transform( object, transformation );
  57. notEqual( transformedObject, object, "object container replaced" );
  58. notEqual( transformedObject.zero, elements[0], "object targeted element replaced" );
  59. equal( transformedObject.one, elements[1], "object untargeted element unchanged" );
  60. equal( object.zero, elements[0], "object original container unchanged" );
  61. equal( object.one, elements[1], "object original container unchanged" );
  62. // Array container
  63. var array = [ elements[0], elements[1] ];
  64. var transformedArray = utility.transform( array, transformation );
  65. notEqual( transformedArray, array, "array container replaced" );
  66. notEqual( transformedArray[0], elements[0], "array targeted element replaced" );
  67. equal( transformedArray[1], elements[1], "array untargeted element unchanged" );
  68. equal( array[0], elements[0], "array original container unchanged" );
  69. equal( array[1], elements[1], "array original container unchanged" );
  70. } );
  71. test( "Container recursion", function() {
  72. // Transformation function
  73. var transformation = function( object ) {
  74. if ( object.replace ) {
  75. return { replaced: true }; // replace Objects having a "replace" field
  76. } else if ( object.container_object ) {
  77. return [ { replace: true } ]; // replace tagged Object containers with Array containers
  78. } else if ( object.container_array ) {
  79. return { zero: { replace: true } }; // replace tagged Array containers with Object containers
  80. } else {
  81. return object;
  82. }
  83. };
  84. // Test elements
  85. var elements = [ { replace: true }, {} ];
  86. // Array to object to element.
  87. var arrayContainingObject = [ { zero: elements[0], one: elements[1] } ];
  88. var transformedArrayContainingObject = utility.transform( arrayContainingObject, transformation );
  89. notEqual( transformedArrayContainingObject, arrayContainingObject, "outer array container replaced" );
  90. notEqual( transformedArrayContainingObject[0], arrayContainingObject[0], "inner object container replaced" );
  91. ok( transformedArrayContainingObject[0].zero.replaced, "contained element replaced" );
  92. equal( arrayContainingObject[0].zero, elements[0], "original container unchanged" );
  93. equal( arrayContainingObject[0].one, elements[1], "original container unchanged" );
  94. // Object to array to element.
  95. var objectContainingArray = { inner: [ elements[0], elements[1] ] };
  96. var transformedObjectContainingArray = utility.transform( objectContainingArray, transformation );
  97. notEqual( transformedObjectContainingArray, objectContainingArray, "outer object container replaced" );
  98. notEqual( transformedObjectContainingArray.inner, objectContainingArray.inner, "inner array container replaced" );
  99. ok( transformedObjectContainingArray.inner[0].replaced, "contained element replaced" );
  100. equal( objectContainingArray.inner[0], elements[0], "original container unchanged" );
  101. equal( objectContainingArray.inner[1], elements[1], "original container unchanged" );
  102. // Object, transformed into array, to element: { container_object: true } => [ { replace: true } ] => [ { replaced: true } ]
  103. var objectBecomingArray = {};
  104. objectBecomingArray.container_object = true;
  105. var transformedObjectBecomingArray = utility.transform( objectBecomingArray, transformation );
  106. notEqual( transformedObjectBecomingArray, objectBecomingArray, "object container replaced" );
  107. ok( transformedObjectBecomingArray[0].replaced, "replacement array container traversed and contained element replaced" );
  108. // Array, transformed into object, to element: [ container_array: true ] => { zero: { replace: true } } => { zero: { replaced: true } }
  109. var arrayBecomingObject = [];
  110. arrayBecomingObject.container_array = true;
  111. var transformedArrayBecomingObject = utility.transform( arrayBecomingObject, transformation );
  112. notEqual( transformedArrayBecomingObject, arrayBecomingObject, "array container replaced" );
  113. ok( transformedArrayBecomingObject.zero.replaced, "replacement object container traversed and contained element replaced" );
  114. } );
  115. // == exceptionMessage ===================================================================
  116. // TODO
  117. // == resolveURI =========================================================================
  118. // TODO
  119. // == xpath.resolve ======================================================================
  120. test ( "XPath resolved", function() {
  121. deepEqual( utility.xpath.resolve( "element()", "root", "start", xpathResolver ), [ "start children matching element()" ], "element()" );
  122. deepEqual( utility.xpath.resolve( "element(*)", "root", "start", xpathResolver ), [ "start children matching element()" ], "element(*)" );
  123. deepEqual( utility.xpath.resolve( "element(a)", "root", "start", xpathResolver ), [ "start children matching element() named a" ], "element(a)" );
  124. deepEqual( utility.xpath.resolve( "element(*,t)", "root", "start", xpathResolver ), [ "start children matching element() typed t" ], "element(*,t)" );
  125. deepEqual( utility.xpath.resolve( "element(a,t)", "root", "start", xpathResolver ), [ "start children matching element() named a typed t" ], "element(a,t)" );
  126. deepEqual( utility.xpath.resolve( "a/child::*", "root", "start", xpathResolver ), [ "start children named a children matching element()" ], "a/child::*" );
  127. deepEqual( utility.xpath.resolve( "a/child::b", "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/child::b" );
  128. deepEqual( utility.xpath.resolve( "a/ancestor::b", "root", "start", xpathResolver ), [ "start children named a ancestors named b" ], "a/ancestor::b" );
  129. deepEqual( utility.xpath.resolve( "a/ancestor-or-self::b", "root", "start", xpathResolver ), [ "start children named a and ancestors named b" ], "a/ancestor-or-self::b" );
  130. deepEqual( utility.xpath.resolve( "a/descendant::b", "root", "start", xpathResolver ), [ "start children named a descendants named b" ], "a/descendant::b" );
  131. deepEqual( utility.xpath.resolve( "a/descendant-or-self::b", "root", "start", xpathResolver ), [ "start children named a and descendants named b" ], "a/descendant-or-self::b" );
  132. deepEqual( utility.xpath.resolve( "//", "root", "start", xpathResolver ), [ "root and descendants" ], "//" );
  133. deepEqual( utility.xpath.resolve( "/", "root", "start", xpathResolver ), [ "root" ], "/" );
  134. deepEqual( utility.xpath.resolve( "", "root", "start", xpathResolver ), [ "start" ], "empty string" );
  135. deepEqual( utility.xpath.resolve( [ "" ], "root", "root", xpathResolver ), [ "root and descendants" ], "//, pre-split" );
  136. deepEqual( utility.xpath.resolve( [], "root", "root", xpathResolver ), [ "root" ], "/, pre-split" );
  137. deepEqual( utility.xpath.resolve( [], "root", "start", xpathResolver ), [ "start" ], "empty string, pre-split" );
  138. deepEqual( utility.xpath.resolve( "'*'", "root", "start", xpathResolver ), [ "start children named *" ], "'*', quoted special character" );
  139. deepEqual( utility.xpath.resolve( "*", "root", "start", xpathResolver ), [ "start children matching element()" ], "*" );
  140. deepEqual( utility.xpath.resolve( "a", "root", "start", xpathResolver ), [ "start children named a" ], "a" );
  141. deepEqual( utility.xpath.resolve( [ "*" ], "root", "start", xpathResolver ), [ "start children matching element()" ], "*, pre-split" );
  142. deepEqual( utility.xpath.resolve( [ "a" ], "root", "start", xpathResolver ), [ "start children named a" ], "a, pre-split" );
  143. deepEqual( utility.xpath.resolve( "a/*", "root", "start", xpathResolver ), [ "start children named a children matching element()" ], "a/*" );
  144. deepEqual( utility.xpath.resolve( "a/b", "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/b" );
  145. deepEqual( utility.xpath.resolve( "a//*", "root", "start", xpathResolver ), [ "start children named a and descendants children matching element()" ], "a//*" );
  146. deepEqual( utility.xpath.resolve( "a//b", "root", "start", xpathResolver ), [ "start children named a and descendants children named b" ], "a//b" );
  147. deepEqual( utility.xpath.resolve( "a/./b", "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/./b" );
  148. deepEqual( utility.xpath.resolve( "a/../b", "root", "start", xpathResolver ), [ "start children named a parent children named b" ], "a/../b" );
  149. deepEqual( utility.xpath.resolve( [ "a", "*" ], "root", "start", xpathResolver ), [ "start children named a children matching element()" ], "a/*, pre-split" );
  150. deepEqual( utility.xpath.resolve( [ "a", "b" ], "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/b, pre-split" );
  151. deepEqual( utility.xpath.resolve( [ "a", "", "*" ], "root", "start", xpathResolver ), [ "start children named a and descendants children matching element()" ], "a//*, pre-split" );
  152. deepEqual( utility.xpath.resolve( [ "a", "", "b" ], "root", "start", xpathResolver ), [ "start children named a and descendants children named b" ], "a//b, pre-split" );
  153. deepEqual( utility.xpath.resolve( [ "a", ".", "b" ], "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/./b, pre-split" );
  154. deepEqual( utility.xpath.resolve( [ "a", "..", "b" ], "root", "start", xpathResolver ), [ "start children named a parent children named b" ], "a/../b, pre-split" );
  155. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "child", kind: "element" } ], "root", "start", xpathResolver ), [ "start children named a children matching element()" ], "a/*, pre-parsed" );
  156. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "child", name: "b" } ], "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/b, pre-parsed" );
  157. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "descendant-or-self", kind: "node" }, { axis: "child", kind: "element" } ], "root", "start", xpathResolver ), [ "start children named a and descendants children matching element()" ], "a//*, pre-parsed" );
  158. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "descendant-or-self", kind: "node" }, { axis: "child", name: "b" } ], "root", "start", xpathResolver ), [ "start children named a and descendants children named b" ], "a//b, pre-parsed" );
  159. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "self", kind: "node" }, { axis: "child", name: "b" } ], "root", "start", xpathResolver ), [ "start children named a children named b" ], "a/./b, pre-parsed" );
  160. deepEqual( utility.xpath.resolve( [ { axis: "child", name: "a" }, { axis: "parent", kind: "node" }, { axis: "child", name: "b" } ], "root", "start", xpathResolver ), [ "start children named a parent children named b" ], "a/../b, pre-parsed" );
  161. deepEqual( utility.xpath.resolve( "@*", "root", "start", xpathResolver ), [ "start attributes matching attribute()" ], "*" );
  162. deepEqual( utility.xpath.resolve( "@r", "root", "start", xpathResolver ), [ "start attributes named r" ], "r" );
  163. deepEqual( utility.xpath.resolve( "a/@*", "root", "start", xpathResolver ), [ "start children named a attributes matching attribute()" ], "*" );
  164. deepEqual( utility.xpath.resolve( "a/@r", "root", "start", xpathResolver ), [ "start children named a attributes named r" ], "r" );
  165. deepEqual( utility.xpath.resolve( "a", "root", [ "alpha" ], xpathResolver ), [ "alpha children named a" ], "a, original context as array" );
  166. deepEqual( utility.xpath.resolve( "a", "root", [ "beta", "gamma" ], xpathResolver ), [ "beta children named a", "gamma children named a" ], "a, plural original context" );
  167. // Examples from http://www.w3.org/TR/xpath20/#unabbrev
  168. deepEqual( utility.xpath.resolve( "child::para", "root", "start", xpathResolver ), [ "start children named para" ], "child::para" );
  169. deepEqual( utility.xpath.resolve( "child::*", "root", "start", xpathResolver ), [ "start children matching element()" ], "child::*" );
  170. deepEqual( utility.xpath.resolve( "child::text()", "root", "start", xpathResolver ), [ "start children matching text()" ], "child::text()" );
  171. deepEqual( utility.xpath.resolve( "child::node()", "root", "start", xpathResolver ), [ "start children" ], "child::node()" );
  172. deepEqual( utility.xpath.resolve( "attribute::name", "root", "start", xpathResolver ), [ "start attributes named name" ], "attribute::name" );
  173. deepEqual( utility.xpath.resolve( "attribute::*", "root", "start", xpathResolver ), [ "start attributes matching attribute()" ], "attribute::*" );
  174. deepEqual( utility.xpath.resolve( "parent::node()", "root", "start", xpathResolver ), [ "start parent" ], "parent::node()" );
  175. deepEqual( utility.xpath.resolve( "descendant::para", "root", "start", xpathResolver ), [ "start descendants named para" ], "descendant::para" );
  176. deepEqual( utility.xpath.resolve( "ancestor::div", "root", "start", xpathResolver ), [ "start ancestors named div" ], "ancestor::div" );
  177. deepEqual( utility.xpath.resolve( "ancestor-or-self::div", "root", "start", xpathResolver ), [ "start and ancestors named div" ], "ancestor-or-self::div" );
  178. deepEqual( utility.xpath.resolve( "descendant-or-self::para", "root", "start", xpathResolver ), [ "start and descendants named para" ], "descendant-or-self::para" );
  179. deepEqual( utility.xpath.resolve( "self::para", "root", "start", xpathResolver ), [ "start named para" ], "self::para" );
  180. deepEqual( utility.xpath.resolve( "child::chapter/descendant::para", "root", "start", xpathResolver ), [ "start children named chapter descendants named para" ], "child::chapter/descendant::para" );
  181. deepEqual( utility.xpath.resolve( "child::*/child::para", "root", "start", xpathResolver ), [ "start children matching element() children named para" ], "child::*/child::para" );
  182. deepEqual( utility.xpath.resolve( "/", "root", "start", xpathResolver ), [ "root" ], "/" );
  183. deepEqual( utility.xpath.resolve( "/descendant::para", "root", "start", xpathResolver ), [ "root descendants named para" ], "/descendant::para" );
  184. deepEqual( utility.xpath.resolve( "/descendant::list/child::member", "root", "start", xpathResolver ), [ "root descendants named list children named member" ], "/descendant::list/child::member" );
  185. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[fn:position() = 1]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[fn:position() = 1]" );
  186. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[fn:position() = fn:last()]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[fn:position() = fn:last()]" );
  187. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[fn:position() = fn:last()-1]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[fn:position() = fn:last()-1]" );
  188. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[fn:position() > 1]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[fn:position() > 1]" );
  189. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "following-sibling::chapter[fn:position() = 1]", "root", "start", xpathResolver ), [ "n/a" ], "following-sibling::chapter[fn:position() = 1]" );
  190. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "preceding-sibling::chapter[fn:position() = 1]", "root", "start", xpathResolver ), [ "n/a" ], "preceding-sibling::chapter[fn:position() = 1]" );
  191. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "/descendant::figure[fn:position() = 42]", "root", "start", xpathResolver ), [ "n/a" ], "/descendant::figure[fn:position() = 42]" );
  192. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "/child::book/child::chapter[fn:position() = 5]/child::section[fn:position() = 2]", "root", "start", xpathResolver ), [ "n/a" ], "/child::book/child::chapter[fn:position() = 5]/child::section[fn:position() = 2]" );
  193. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[attribute::type eq 'warning]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[attribute::type eq 'warning]" );
  194. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[attribute::type eq 'warning'][fn:position() = 5]", "root", "start", xpathResolver ), [ "n/a" ], "child::para[attribute::type eq 'warning'][fn:position() = 5]" );
  195. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::para[fn:position() = 5][attribute::type eq 'warning']", "root", "start", xpathResolver ), [ "n/a" ], "child::para[fn:position() = 5][attribute::type eq 'warning']" );
  196. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::chapter[child::title = 'Introduction']", "root", "start", xpathResolver ), [ "n/a" ], "child::chapter[child::title = 'Introduction']" );
  197. // n/a: predicate -- deepEqual( utility.xpath.resolve( "child::chapter[child::title]", "root", "start", xpathResolver ), [ "n/a" ], "child::chapter[child::title]" );
  198. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::*[self::chapter or self::appendix]", "root", "start", xpathResolver ), [ "n/a" ], "child::*[self::chapter or self::appendix]" );
  199. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "child::*[self::chapter or self::appendix][fn:position() = fn:last()]", "root", "start", xpathResolver ), [ "n/a" ], "child::*[self::chapter or self::appendix][fn:position() = fn:last()]" );
  200. // Examples from http://www.w3.org/TR/xpath20/#abbrev
  201. deepEqual( utility.xpath.resolve( "para", "root", "start", xpathResolver ), [ "start children named para" ], "para" );
  202. deepEqual( utility.xpath.resolve( "*", "root", "start", xpathResolver ), [ "start children matching element()" ], "*" );
  203. deepEqual( utility.xpath.resolve( "text()", "root", "start", xpathResolver ), [ "start children matching text()" ], "text()" );
  204. deepEqual( utility.xpath.resolve( "@name", "root", "start", xpathResolver ), [ "start attributes named name" ], "@name" );
  205. deepEqual( utility.xpath.resolve( "@*", "root", "start", xpathResolver ), [ "start attributes matching attribute()" ], "@*" );
  206. // n/a: predicate -- deepEqual( utility.xpath.resolve( "para[1]", "root", "start", xpathResolver ), [ "n/a" ], "para[1]" );
  207. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "para[fn:last()]", "root", "start", xpathResolver ), [ "n/a" ], "para[fn:last()]" );
  208. deepEqual( utility.xpath.resolve( "*/para", "root", "start", xpathResolver ), [ "start children matching element() children named para" ], "*/para" );
  209. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "/book/chapter[5]/section[2]", "root", "start", xpathResolver ), [ "n/a" ], "/book/chapter[5]/section[2]" );
  210. deepEqual( utility.xpath.resolve( "chapter//para", "root", "start", xpathResolver ), [ "start children named chapter and descendants children named para" ], "chapter//para" );
  211. deepEqual( utility.xpath.resolve( "//para", "root", "start", xpathResolver ), [ "root and descendants children named para" ], "//para" );
  212. deepEqual( utility.xpath.resolve( "//@version", "root", "start", xpathResolver ), [ "root and descendants attributes named version" ], "//@version" );
  213. deepEqual( utility.xpath.resolve( "//list/member", "root", "start", xpathResolver ), [ "root and descendants children named list children named member" ], "//list/member" );
  214. deepEqual( utility.xpath.resolve( ".//para", "root", "start", xpathResolver ), [ "start and descendants children named para" ], ".//para" );
  215. deepEqual( utility.xpath.resolve( "..", "root", "start", xpathResolver ), [ "start parent" ], ".." );
  216. deepEqual( utility.xpath.resolve( "../@lang", "root", "start", xpathResolver ), [ "start parent attributes named lang" ], "../@lang" );
  217. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "para[@type='warning']", "root", "start", xpathResolver ), [ "n/a" ], "para[@type='warning']" );
  218. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "para[@type='warning'][5]", "root", "start", xpathResolver ), [ "n/a" ], "para[@type='warning'][5]" );
  219. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "para[5][@type='warning']", "root", "start", xpathResolver ), [ "n/a" ], "para[5][@type='warning']" );
  220. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "chapter[title='Introduction']", "root", "start", xpathResolver ), [ "n/a" ], "chapter[title='Introduction']" );
  221. // n/a: predicate -- deepEqual( utility.xpath.resolve( "chapter[title]", "root", "start", xpathResolver ), [ "n/a" ], "chapter[title]" );
  222. // n/a: predicate+expression -- deepEqual( utility.xpath.resolve( "employee[@secretary and @assistant]", "root", "start", xpathResolver ), [ "n/a" ], "employee[@secretary and @assistant]" );
  223. // n/a: expression -- deepEqual( utility.xpath.resolve( "book/(chapter|appendix)/section", "root", "start", xpathResolver ), [ "n/a" ], "book/(chapter|appendix)/section" );
  224. } );
  225. // == xpath.parse ========================================================================
  226. test( "XPath parsed", function() {
  227. var result = [ { axis: "child", kind: undefined, name: "a", type: undefined }, { axis: "child", kind: undefined, name: "b", type: undefined } ];
  228. deepEqual( utility.xpath.parse( "a/b" ), result, "string" );
  229. deepEqual( utility.xpath.parse( [ "a", "b" ] ), result, "pre-split" );
  230. deepEqual( utility.xpath.parse( result ), result, "pre-parsed" );
  231. } );
  232. // == xpath.parseStep ====================================================================
  233. test( "XPath steps", function() {
  234. deepEqual( utility.xpath.parseStep( "" ), { axis: "descendant-or-self", kind: "node", name: undefined, type: undefined }, "AbbreviatedRelativeLocationPath" ); // http://www.w3.org/TR/xpath/#NT-AbbreviatedRelativeLocationPath
  235. deepEqual( utility.xpath.parseStep( "." ), { axis: "self", kind: "node", name: undefined, type: undefined }, "AbbreviatedStep, self" ); // http://www.w3.org/TR/xpath/#NT-AbbreviatedStep
  236. deepEqual( utility.xpath.parseStep( ".." ), { axis: "parent", kind: "node", name: undefined, type: undefined }, "AbbreviatedStep, parent" ); // http://www.w3.org/TR/xpath/#NT-AbbreviatedStep
  237. deepEqual( utility.xpath.parseStep( "child::a" ), { axis: "child", kind: undefined, name: "a", type: undefined }, "AxisSpecifier, child" ); // http://www.w3.org/TR/xpath/#NT-AxisSpecifier
  238. deepEqual( utility.xpath.parseStep( "a" ), { axis: "child", kind: undefined, name: "a", type: undefined }, "AbbreviatedAxisSpecifier, child" ); // http://www.w3.org/TR/xpath/#NT-AbbreviatedAxisSpecifier
  239. deepEqual( utility.xpath.parseStep( "attribute::a" ), { axis: "attribute", kind: undefined, name: "a", type: undefined }, "AxisSpecifier, attribute" ); // http://www.w3.org/TR/xpath/#NT-AxisSpecifier
  240. deepEqual( utility.xpath.parseStep( "@a" ), { axis: "attribute", kind: undefined, name: "a", type: undefined }, "AbbreviatedAxisSpecifier, attribute" ); // http://www.w3.org/TR/xpath/#NT-AbbreviatedAxisSpecifier
  241. deepEqual( utility.xpath.parseStep( "child::*" ), { axis: "child", kind: "element", name: undefined, type: undefined }, "AxisSpecifier+NameTest, wildcard" ); // http://www.w3.org/TR/xpath/#NT-NameTest
  242. deepEqual( utility.xpath.parseStep( "*" ), { axis: "child", kind: "element", name: undefined, type: undefined }, "AbbreviatedAxisSpecifier+NameTest, wildcard" ); // http://www.w3.org/TR/xpath/#NT-NameTest
  243. deepEqual( utility.xpath.parseStep( "node()" ), { axis: "child", kind: "node", name: undefined, type: undefined }, "KindTest" ); // http://www.w3.org/TR/xpath20/#prod-xpath-KindTest
  244. deepEqual( utility.xpath.parseStep( "node(b)" ), { axis: "child", kind: "node", name: "b", type: undefined }, "KindTest, node name" ); // http://www.w3.org/TR/xpath20/#prod-xpath-KindTest
  245. deepEqual( utility.xpath.parseStep( "node(b,t)" ), { axis: "child", kind: "node", name: "b", type: "t" }, "KindTest, node name, node type" ); // http://www.w3.org/TR/xpath20/#prod-xpath-KindTest
  246. deepEqual( utility.xpath.parseStep( "'a+b'" ), { axis: "child", kind: undefined, name: "a+b", type: undefined }, "quoted NameTest, single quotes" );
  247. deepEqual( utility.xpath.parseStep( "'a\\'\"\\+\"\\'b'" ), { axis: "child", kind: undefined, name: "a'\"\\+\"'b", type: undefined }, "quoted NameTest, single quotes with embedded escapes" );
  248. deepEqual( utility.xpath.parseStep( '"a+a"' ), { axis: "child", kind: undefined, name: "a+a", type: undefined }, "quoted NameTest, double quotes" );
  249. deepEqual( utility.xpath.parseStep( '"a\'\\"\\+\\"\'a"' ), { axis: "child", kind: undefined, name: "a'\"\\+\"'a", type: undefined }, "quoted NameTest, double quotes with embedded escapes" );
  250. deepEqual( utility.xpath.parseStep( 'node("b+b")' ), { axis: "child", kind: "node", name: "b+b", type: undefined }, "KindTest, quoted node name" );
  251. deepEqual( utility.xpath.parseStep( 'node(b,"http://vwf.example.com/node.vwf")' ), { axis: "child", kind: "node", name: "b", type: "http://vwf.example.com/node.vwf" }, "KindTest, node name, quoted node type" );
  252. deepEqual( utility.xpath.parseStep( '"."' ), { axis: "child", kind: undefined, name: ".", type: undefined }, "quoted NameTest, xpath token: abbreviated step self" );
  253. deepEqual( utility.xpath.parseStep( '".."' ), { axis: "child", kind: undefined, name: "..", type: undefined }, "quoted NameTest, xpath token: abbreviated step parent" );
  254. deepEqual( utility.xpath.parseStep( '"@a"' ), { axis: "child", kind: undefined, name: "@a", type: undefined }, "quoted NameTest, xpath token: abbreviated axis attribute" );
  255. deepEqual( utility.xpath.parseStep( '"axis::"' ), { axis: "child", kind: undefined, name: "axis::", type: undefined }, "quoted NameTest, xpath token: axis" );
  256. deepEqual( utility.xpath.parseStep( '"*"' ), { axis: "child", kind: undefined, name: "*", type: undefined }, "quoted NameTest, xpath token: wildcard" );
  257. } );
  258. // == xpath.parsePredicate ===============================================================
  259. test( "XPath predicates", function() {
  260. var xpath;
  261. deepEqual( utility.xpath.parsePredicate( "" ), undefined, "empty" );
  262. deepEqual( utility.xpath.parsePredicate( "a" ), undefined, "step" );
  263. deepEqual( utility.xpath.parsePredicate( "/" ), undefined, "separator" );
  264. xpath = "[a]";
  265. deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "a", type: undefined } ], "string" );
  266. xpath = { string: "[a]", index: 0 };
  267. deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "a", type: undefined } ], "object" );
  268. deepEqual( xpath, { string: "[a]", index: 3 }, "object" );
  269. xpath = { string: "['[]']", index: 0 };
  270. deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "[]", type: undefined } ], "quoted brackets" );
  271. deepEqual( xpath, { string: "['[]']", index: 6 }, "quoted brackets" );
  272. xpath = { string: "[a][b]", index: 0 };
  273. deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "a", type: undefined } ], "predicate terminator" );
  274. deepEqual( xpath, { string: "[a][b]", index: 3 }, "predicate terminator" );
  275. // not implemented: xpath = { string: "[a[b]]", index: 0 };
  276. // not implemented: deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "a", type: undefined, predicates: [ { axis: "child", kind: undefined, name: "b", type: undefined } ] } ], "nested" );
  277. // not implemented: deepEqual( xpath, { string: "[a[b]]", index: 6 }, "nested" );
  278. xpath = { string: "[a]/", index: 0 };
  279. deepEqual( utility.xpath.parsePredicate( xpath ), [ { axis: "child", kind: undefined, name: "a", type: undefined } ], "separator terminator" );
  280. deepEqual( xpath, { string: "[a]/", index: 3 }, "separator terminator" );
  281. } );
  282. // == xpath.parseSeparator ===============================================================
  283. test( "XPath separator", function() {
  284. var xpath;
  285. xpath = "/";
  286. equal( utility.xpath.parseSeparator( xpath ), true, "string" );
  287. xpath = { string: "/", index: 0 };
  288. equal( utility.xpath.parseSeparator( xpath ), true, "object" );
  289. xpath = { string: "/", index: 0 };
  290. equal( utility.xpath.parseSeparator( xpath ), true, "single" );
  291. deepEqual( xpath, { string: "/", index: 1 }, "single" );
  292. xpath = { string: "a/b", index: 1 };
  293. equal( utility.xpath.parseSeparator( xpath ), true, "middle" );
  294. deepEqual( xpath, { string: "a/b", index: 2 }, "middle" );
  295. xpath = { string: "a", index: 0 };
  296. equal( utility.xpath.parseSeparator( xpath ), undefined, "missing" );
  297. deepEqual( xpath, { string: "a", index: 0 }, "missing" );
  298. xpath = { string: "", index: 0 };
  299. equal( utility.xpath.parseSeparator( xpath ), undefined, "empty" );
  300. deepEqual( xpath, { string: "", index: 0 }, "empty" );
  301. } );
  302. // == xpath.quoteName, xpath.unquoteName =================================================
  303. test( "XPath quoting", function() {
  304. deepEqual( utility.xpath.quoteName( 'a' ), '"a"', "plain" );
  305. deepEqual( utility.xpath.quoteName( "'a'" ), '"\'a\'"', "embedded single quote" );
  306. deepEqual( utility.xpath.quoteName( '"a"' ), '"\\"a\\""', "embedded double quote" );
  307. deepEqual( utility.xpath.quoteName( '\\a\\' ), '"\\\\a\\\\"', "embedded escape" );
  308. deepEqual( utility.xpath.unquoteName( '"a"' ), 'a', "plain" );
  309. deepEqual( utility.xpath.unquoteName( '"\'a\'"' ), "'a'", "embedded single quote" );
  310. deepEqual( utility.xpath.unquoteName( '"\\"a\\""' ), '"a"', "embedded double quote" );
  311. deepEqual( utility.xpath.unquoteName( '"\\\\a\\\\"' ), '\\a\\', "embedded escape" );
  312. } );
  313. // == Helper functions ===================================================================
  314. // Resolve an xpath step with a description of the work to be done.
  315. function xpathResolver( step, id ) {
  316. var result = id;
  317. switch ( step.axis ) {
  318. // case "preceding":
  319. // case "preceding-sibling":
  320. case "ancestor-or-self":
  321. result += " and ancestors";
  322. break;
  323. case "ancestor":
  324. result += " ancestors";
  325. break;
  326. case "parent":
  327. result += " parent";
  328. break;
  329. case "self":
  330. break;
  331. case "child":
  332. result += " children";
  333. break;
  334. case "descendant":
  335. result += " descendants";
  336. break;
  337. case "descendant-or-self":
  338. result += " and descendants";
  339. break;
  340. // case "following-sibling":
  341. // case "following":
  342. case "attribute":
  343. result += " attributes";
  344. break;
  345. // case "namespace":
  346. }
  347. if ( step.kind && step.kind != "node" ) {
  348. result += " matching " + step.kind + "()";
  349. }
  350. if ( step.name ) result += " named " + step.name;
  351. if ( step.type ) result += " typed " + step.type;
  352. return [ result ];
  353. }
  354. } );
  355. } );
  356. </script>
  357. <link rel="stylesheet" type="text/css" href="qunit.css" />
  358. </head>
  359. <body>
  360. <h1 id="qunit-header">Virtual World Framework</h1>
  361. <h2 id="qunit-banner"></h2>
  362. <div id="qunit-testrunner-toolbar"></div>
  363. <h2 id="qunit-userAgent"></h2>
  364. <ol id="qunit-tests"></ol>
  365. <div id="qunit-fixture">test markup, will be hidden</div>
  366. </body>
  367. </html>