tree.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
  2. // (C) Copyright 2003-2007 Jonathan Turkanis
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
  5. //--------------Event-handlers------------------------------------------------//
  6. function toggle(id) { get_tree().find(id).toggle(); }
  7. function blur_tree()
  8. {
  9. if ( window.event &&
  10. window.event.srcElement &&
  11. window.event.srcElement.blur &&
  12. window.event.srcElement != document.body )
  13. window.event.srcElement.blur();
  14. else if (target_frame())
  15. target_frame().window.focus();
  16. }
  17. document.onclick = blur_tree;
  18. //--------------Netscape 4.x-specific-----------------------------------------//
  19. window.saved_width = window.outerWidth;
  20. function reload_tree()
  21. {
  22. if (window.outerWidth != window.saved_width)
  23. window.location.reload();
  24. }
  25. if (window.Event && Event.RESIZE) {
  26. window.captureEvents(Event.RESIZE);
  27. window.onResize = reload_tree;
  28. }
  29. //--------------Functions for browser-sniffing--------------------------------//
  30. function major_version(app)
  31. {
  32. var index = navigator.userAgent.indexOf(app);
  33. if (index == -1)
  34. return -1;
  35. return parseInt(navigator.userAgent.charAt(index + app.length + 1));
  36. }
  37. function dom_support()
  38. {
  39. if (dom_support.cache == null)
  40. dom_support.cache = dom_support_impl();
  41. return dom_support.cache;
  42. }
  43. function dom_support_impl()
  44. {
  45. var version;
  46. if ( (version = major_version("Mozilla")) != -1 &&
  47. navigator.userAgent.indexOf("compatible") == -1 )
  48. return version > 4;
  49. if ((version = major_version("Opera")) != -1)
  50. return version > 6;
  51. if ((version = major_version("Konqueror")) != -1)
  52. return version > 2;
  53. if ((version = major_version("Links")) != -1)
  54. return false;
  55. return document.getElementById || document.all;
  56. }
  57. //--------------Utility functions---------------------------------------------//
  58. function target_frame()
  59. {
  60. return get_tree() ? top.frames[get_tree().target] : null;
  61. }
  62. function get_tree() { return document.tree_control; }
  63. function static_display() { return !dom_support() || get_tree().dump_html; }
  64. function elt_by_id(id)
  65. {
  66. return document.all ?
  67. document.all[id] :
  68. document.getElementById ?
  69. document.getElementById(id) :
  70. null;
  71. }
  72. function replace_query(url, query)
  73. {
  74. var pos;
  75. if ((pos = url.indexOf("?")) != -1)
  76. url = url.substring(0, pos);
  77. return url + "?" + query;
  78. }
  79. //--------------Functions for HTML-generation---------------------------------//
  80. function icon_char(state)
  81. {
  82. return state == tree_node.expanded ?
  83. "-" :
  84. state == tree_node.collapsed ?
  85. "+" :
  86. " ";
  87. }
  88. function html_list(id, display, margin)
  89. {
  90. return "<div id='" + id + "' style='white-space:nowrap;display:" +
  91. display + "'>";
  92. }
  93. function html_list_item(content)
  94. {
  95. return "\n<div class='tree-item'>" + content + "</div>";
  96. }
  97. function html_anchor(content, cl, href, target)
  98. {
  99. return "<a class='" + cl + "' onfocus='blur_tree()" +
  100. "' href='" + href + "'" +
  101. (target ? " target='" + target + "'" : "") +
  102. ">" + content + "</a>";
  103. }
  104. //--------------Definition of class tree_node---------------------------------//
  105. function tree_node__add(text_or_node, link_or_hide, hide)
  106. {
  107. if (this.state == tree_node.neutral)
  108. this.state = tree_node.collapsed;
  109. var k;
  110. if (text_or_node.length != null) {
  111. k = new tree_node(text_or_node, link_or_hide);
  112. k.hide_kids = hide != null ? hide : false;
  113. } else {
  114. k = text_or_node;
  115. k.hide_kids = link_or_hide != null ? link_or_hide : false;
  116. }
  117. k.mom = this;
  118. if (this.kids == null)
  119. this.kids = new Array();
  120. this.kids[this.kids.length] = k;
  121. return k;
  122. }
  123. function tree_node__level()
  124. {
  125. var level;
  126. var node;
  127. for (node = this.mom, level = -1; node; node = node.mom, ++level)
  128. ;
  129. return level;
  130. }
  131. function tree_node__parent() { return this.mom; }
  132. function tree_node__print()
  133. {
  134. var icon =
  135. !static_display() ?
  136. "<span style='font-family:monospace' class='tree-icon' id='icon" +
  137. this.id + "'>" + icon_char(this.state) + "</span>&nbsp;&nbsp;" :
  138. "";
  139. var handler =
  140. !static_display() && this.kids ?
  141. "javascript:toggle(\"id" + this.id + "\")" :
  142. "";
  143. var text = "<span class='tree-text'>" + this.text + "</span>"
  144. var tree = get_tree();
  145. var indent = tree.indent * this.level();
  146. return html_list_item(
  147. "<div style='margin-left:" + (2 * indent) +
  148. ";text-indent:-" + indent + "'>" +
  149. ( !tree.dump_html ?
  150. this.kids ?
  151. html_anchor(icon, "tree-icon", handler) :
  152. icon :
  153. "" ) +
  154. ( tree.numbered ?
  155. "" + "<span class='tree-label'>" +
  156. this.id.substring(1) + "</span>" :
  157. "" ) +
  158. "&nbsp;&nbsp;" +
  159. ( this.link ?
  160. html_anchor( text, "tree-text", this.link,
  161. tree.target ) :
  162. text ) +
  163. "</div>" +
  164. this.print_kids()
  165. );
  166. }
  167. function tree_node__print_kids(margin)
  168. {
  169. var result = "";
  170. if (this.kids != null && (!static_display() || !this.hide_kids)) {
  171. if (margin == null)
  172. margin = get_tree().indent;
  173. result += html_list( "list" + this.id,
  174. this.state == tree_node.collapsed &&
  175. !static_display()
  176. ? "none" : "",
  177. margin );
  178. for (var z = 0; z < this.kids.length; ++z) {
  179. var k = this.kids[z];
  180. k.id = this.id + "." + (z + 1);
  181. result += k.print();
  182. }
  183. result += "</div>";
  184. }
  185. return result;
  186. }
  187. function tree_node__toggle(expand)
  188. {
  189. if ( static_display() ||
  190. this.kids == null ||
  191. expand != null && expand ==
  192. (this.state == tree_node.expanded) )
  193. {
  194. return;
  195. }
  196. this.state =
  197. this.state == tree_node.expanded ?
  198. tree_node.collapsed :
  199. tree_node.expanded;
  200. elt_by_id("icon" + this.id).innerHTML =
  201. icon_char(this.state);
  202. elt_by_id("list" + this.id).style.display =
  203. this.state == tree_node.expanded ?
  204. "" :
  205. "none";
  206. }
  207. function add_methods(obj)
  208. {
  209. obj.add = tree_node__add;
  210. obj.level = tree_node__level;
  211. obj.parent = tree_node__parent;
  212. obj.print = tree_node__print;
  213. obj.print_kids = tree_node__print_kids;
  214. obj.toggle = tree_node__toggle;
  215. }
  216. function tree_node(text, link)
  217. {
  218. // Member data
  219. this.text = text;
  220. this.link = link;
  221. this.mom = null;
  222. this.kids = null;
  223. this.id = null;
  224. this.state = 0; // Neutral.
  225. if (!add_methods.prototype)
  226. add_methods(this);
  227. }
  228. tree_node.neutral = 0;
  229. tree_node.expanded = 1;
  230. tree_node.collapsed = 2;
  231. if (tree_node.prototype)
  232. add_methods(tree_node.prototype);
  233. //--------------Definition of class tree_control------------------------------//
  234. function tree_control__add(text, link, hide)
  235. {
  236. return this.root.add(text, link, hide);
  237. }
  238. function tree_control__draw()
  239. {
  240. var tree = get_tree();
  241. var dom = dom_support();
  242. var element = dom ? elt_by_id('tree_control') : null;
  243. if (element || !dom || tree.drawn) {
  244. var html = tree.html();
  245. if (tree.dump_html) {
  246. var pat = new RegExp("<", "g");
  247. html = "<pre>" + html.replace(pat, "&lt;") + "</pre>";
  248. if (document.body.innerHTML)
  249. document.body.innerHTML = html;
  250. else
  251. document.write(html);
  252. } else if (dom) {
  253. element.innerHTML = html;
  254. } else {
  255. document.open();
  256. document.write(
  257. "<body>" + html +
  258. ( major_version("MSIE") == 3 ?
  259. "<noscript>" :
  260. document.layers ?
  261. "<layer visibility='hide'>" :
  262. "<table width=0 height=0 style='" +
  263. "visibility:hidden;display:none'><tr><td>" )
  264. );
  265. document.close();
  266. }
  267. tree.drawn = true;
  268. tree.load();
  269. } else {
  270. var t = navigator.userAgent.indexOf("Clue") != -1 ? 500 : 100;
  271. setTimeout("tree_control__draw()", t);
  272. }
  273. }
  274. function tree_control__find(id)
  275. {
  276. var indices = id.split(".");
  277. var node = this.root;
  278. for (var z = 1; z < indices.length; ++z)
  279. node = node.kids[indices[z] - 1];
  280. return node;
  281. }
  282. function tree_control__html()
  283. {
  284. return "<table><tr><td align='left'><table width=150><tr><td>" +
  285. "<h1 class=tree-caption>" + this.caption + "</h1></td></tr>" +
  286. ( !static_display() ?
  287. "<tr><td><p class='tree-sync'><a title='reload current " +
  288. "page with a url suitable for bookmarking' class=" +
  289. "'tree-sync' href='javascript:get_tree().sync()'>" +
  290. "[link to this page]</a></p></td></tr>" :
  291. "" ) +
  292. "</table></td></tr><tr><td>" + this.root.print_kids(0) +
  293. "</td></tr></table>";
  294. }
  295. function load_target(url)
  296. {
  297. var target;
  298. if ((target = target_frame()) && target.location.href != url)
  299. target.location.replace(url);
  300. else {
  301. setTimeout("load_target('" + url + "')", 100);
  302. }
  303. }
  304. function tree_control__load()
  305. {
  306. var query;
  307. if ((query = top.location.search).length == 0)
  308. return;
  309. query = query.substring(1);
  310. var eq;
  311. if ((eq = query.indexOf("=")) != -1) {
  312. if (query.substring(0, 4) == "page") {
  313. load_target(unescape(query.substring(eq + 1)));
  314. return;
  315. }
  316. query = query.substring(eq + 1);
  317. }
  318. var indices = query.split(".");
  319. if (!indices.length)
  320. return;
  321. this.reset();
  322. var node = this.root;
  323. for (var z = 0; z < indices.length; ++z) {
  324. var i = parseInt(indices[z]) - 1;
  325. if (!node.kids || i < 0 || node.kids.length <= i)
  326. break;
  327. node = node.kids[i];
  328. node.toggle(/*z != indices.length - 1*/);
  329. }
  330. if (node.link)
  331. load_target(node.link);
  332. }
  333. function tree_control__recurse(op)
  334. {
  335. var stack = new Array();
  336. stack[stack.length] = this.root;
  337. while (stack.length) {
  338. var node = stack[stack.length - 1];
  339. stack.length -=1 ; // Konqueror 2.
  340. op(node);
  341. if (node.kids)
  342. for (var z = 0; z < node.kids.length; ++z)
  343. stack[stack.length] = node.kids[z];
  344. }
  345. }
  346. function tree_control__reset()
  347. {
  348. if (!dom_support())
  349. return;
  350. this.recurse(new Function("x", "if (x.parent()) x.toggle(false);"));
  351. }
  352. function sync_node(node)
  353. {
  354. if (!node.link)
  355. return;
  356. var tgt = target_frame().location.href;
  357. var pos;
  358. if ((pos = tgt.indexOf("?")) != -1)
  359. tgt = tgt.substring(0, pos);
  360. if (node.link.indexOf("://") != -1) {
  361. if (node.link != tgt)
  362. return;
  363. } else {
  364. var base = window.location.href;
  365. if ((pos = base.lastIndexOf("/")) != -1)
  366. base = base.substring(0, pos + 1);
  367. if (base + node.link != tgt)
  368. return;
  369. }
  370. window.success = true;
  371. var href = replace_query( get_tree().top_url,
  372. "path=" + node.id.substring(1) );
  373. top.location.replace(href);
  374. }
  375. function tree_control__sync()
  376. {
  377. if (!dom_support() || self == top)
  378. return;
  379. window.success = false;
  380. get_tree().recurse(sync_node);
  381. if (!window.success)
  382. top.location.replace(
  383. replace_query( get_tree().top_url,
  384. "page=" + escape(target_frame().location.href) )
  385. );
  386. }
  387. function tree_control(target)
  388. {
  389. // Member data
  390. this.root = new tree_node("");
  391. this.target = target ? target : "_self";
  392. this.dump_html = false;
  393. this.caption = "Contents";
  394. this.numbered = true;
  395. this.indent = 15;
  396. this.drawn = false;
  397. this.top_url = top.location.href; // For Opera.
  398. this.root.state = tree_node.expanded;
  399. this.root.id = "";
  400. // Member functions
  401. this.add = tree_control__add;
  402. this.draw = tree_control__draw;
  403. this.find = tree_control__find;
  404. this.html = tree_control__html;
  405. this.load = tree_control__load;
  406. this.recurse = tree_control__recurse;
  407. this.reset = tree_control__reset;
  408. this.sync = tree_control__sync;
  409. document.tree_control = this;
  410. }
  411. tree_control.sync = tree_control__sync;