/ddoc/candydoc/tree.js

http://github.com/baryluk/cords · JavaScript · 374 lines · 296 code · 57 blank · 21 comment · 48 complexity · 02d45da25624946c40d4748d9abc3b42 MD5 · raw file

  1. /* This file is a part of CanDyDOC fileset.
  2. File is written by Victor Nakoryakov and placed into the public domain.
  3. This file is javascript with classes that represents native style tree control. */
  4. var pmNone = 0;
  5. var pmPlus = 1;
  6. var pmMinus = 2;
  7. var hlNone = 0;
  8. var hlGrey = 1;
  9. var hlSelected = 2;
  10. function TreeView(hrefMode)
  11. {
  12. this.domEntry = document.createElement("div");
  13. this.children = new Array();
  14. this.selection = null;
  15. this.hrefMode = hrefMode;
  16. this.createBranch = function(text, iconSrc)
  17. {
  18. var root = new TreeNode(text, iconSrc, this.hrefMode);
  19. root.owner = this;
  20. this.children[ this.children.length ] = root;
  21. this.domEntry.appendChild( root.domEntry );
  22. return root;
  23. }
  24. this.branch = function(text)
  25. {
  26. var ret = null;
  27. for (var i = 0; i < this.children.length; ++i)
  28. if (this.children[i].textElement.data == text)
  29. {
  30. ret = this.children[i];
  31. break;
  32. }
  33. return ret;
  34. }
  35. this.domEntry.style.fontSize = "10px";
  36. this.domEntry.style.cursor = "default";
  37. this.domEntry.style.whiteSpace = "nowrap";
  38. }
  39. var idCounter = 0;
  40. function TreeNode(text, iconSrc, hrefMode)
  41. {
  42. this.id = idCounter++;
  43. this.parentNode = null;
  44. this.children = new Array();
  45. this.domEntry = document.createElement("div");
  46. this.icon = document.createElement("img");
  47. this.textElement = document.createTextNode(text);
  48. this.textSpan = document.createElement("span");
  49. this.lineDiv = document.createElement("div");
  50. this.hierarchyImgs = new Array();
  51. this.onclick = null;
  52. function createIcon()
  53. {
  54. var img = document.createElement("img");
  55. img.style.verticalAlign = "middle";
  56. img.style.position = "relative";
  57. img.style.top = "-1px";
  58. img.width = 16;
  59. img.height = 16;
  60. return img;
  61. }
  62. function createHierarchyImage()
  63. {
  64. var img = createIcon();
  65. img.pointsTop = false;
  66. img.pointsBottom = false;
  67. img.pointsRight = false;
  68. img.pmState = pmNone;
  69. return img;
  70. }
  71. function genHierarchyImageSrc(hierarchyImg)
  72. {
  73. var name = "";
  74. if (hierarchyImg.pointsTop)
  75. name += "t";
  76. if (hierarchyImg.pointsBottom)
  77. name += "b";
  78. if (hierarchyImg.pointsRight)
  79. name += "r";
  80. if (hierarchyImg.pmState == pmPlus)
  81. name += "p";
  82. else if (hierarchyImg.pmState == pmMinus)
  83. name += "m";
  84. if (name == "")
  85. name = "shim";
  86. return "candydoc/img/tree/" + name + ".gif";
  87. }
  88. function setSrc(icon, src)
  89. {
  90. icon.src = src;
  91. // After src change width and height are reseted in IE.
  92. // Bug workaround:
  93. icon.width = 16;
  94. icon.height = 16;
  95. }
  96. this.createChild = function(text, iconSrc)
  97. {
  98. var child = new TreeNode(text, iconSrc, this.owner.hrefMode);
  99. this.children[ this.children.length ] = child;
  100. this.domEntry.appendChild( child.domEntry );
  101. child.parentNode = this;
  102. child.owner = this.owner;
  103. // insert hierarchy images according to deepness level
  104. // of created child.
  105. if (this.children.length > 1)
  106. {
  107. // there were already added child before. So copy `level-1`
  108. // hierarchy images from it.
  109. var prevAddedChild = this.children[ this.children.length - 2 ];
  110. for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i)
  111. {
  112. var prevAddedChildImg = prevAddedChild.hierarchyImgs[i];
  113. var img = createHierarchyImage();
  114. setSrc(img, prevAddedChildImg.src);
  115. img.pointsTop = prevAddedChildImg.pointsTop;
  116. img.pointsBottom = prevAddedChildImg.pointsBottom;
  117. img.pointsRight = prevAddedChildImg.pointsRight;
  118. img.pmState = prevAddedChildImg.pmState;
  119. child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
  120. child.lineDiv.insertBefore(img, child.icon);
  121. }
  122. // change last hierarchy image of prevAddedChild from |_ to |-
  123. var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ];
  124. lastHierarchyImg.pointsBottom = true;
  125. setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
  126. // change hierarchy images of prevAddedChild's children on it's last
  127. // level to |
  128. prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1);
  129. }
  130. else
  131. {
  132. // this is a first child. So copy `level-2`
  133. // hierarchy images from parent, i.e. this.
  134. for (var i = 0; i < this.hierarchyImgs.length - 1; ++i)
  135. {
  136. var parentImg = this.hierarchyImgs[i];
  137. var img = createHierarchyImage();
  138. setSrc(img, parentImg.src);
  139. img.pointsTop = parentImg.pointsTop;
  140. img.pointsBottom = parentImg.pointsBottom;
  141. img.pointsRight = parentImg.pointsRight;
  142. img.pmState = parentImg.pmState;
  143. child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
  144. child.lineDiv.insertBefore(img, child.icon);
  145. }
  146. if (this.hierarchyImgs.length > 0) // we are not root
  147. {
  148. // change last hierarchy image of parent (i.e. this): add minus to it
  149. var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1];
  150. lastHierarchyImg.pmState = pmMinus;
  151. setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
  152. lastHierarchyImg.owner = this;
  153. lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);");
  154. // make decision on image on `level-1`. It depends on parent's (ie this)
  155. // image on same level.
  156. var parentL1HierarchyImg = lastHierarchyImg;
  157. var l1HierarchyImg = createHierarchyImage();
  158. if (parentL1HierarchyImg.pointsBottom)
  159. {
  160. l1HierarchyImg.pointsTop = true;
  161. l1HierarchyImg.pointsBottom = true;
  162. }
  163. setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg));
  164. child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg;
  165. child.lineDiv.insertBefore(l1HierarchyImg, child.icon);
  166. }
  167. }
  168. // in any case on last level our child will have icon |_
  169. var img = createHierarchyImage();
  170. img.pointsTop = true;
  171. img.pointsRight = true;
  172. setSrc(img, genHierarchyImageSrc(img));
  173. child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
  174. child.lineDiv.insertBefore(img, child.icon);
  175. return child;
  176. }
  177. this.lastChild = function()
  178. {
  179. return this.children[ this.children.length - 1 ];
  180. }
  181. this.child = function(text)
  182. {
  183. var ret = null;
  184. for (var i = 0; i < this.children.length; ++i)
  185. if (this.children[i].textElement.data == text)
  186. {
  187. ret = this.children[i];
  188. break;
  189. }
  190. return ret;
  191. }
  192. this.addHierarchyTBLine = function(level)
  193. {
  194. for (var i = 0; i < this.children.length; ++i)
  195. {
  196. var img = this.children[i].hierarchyImgs[level];
  197. img.pointsTop = true;
  198. img.pointsBottom = true;
  199. setSrc(img, genHierarchyImageSrc(img));
  200. this.children[i].addHierarchyTBLine(level);
  201. }
  202. }
  203. this.expand = function()
  204. {
  205. var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
  206. if (img.pmState == pmPlus)
  207. {
  208. img.pmState = pmMinus;
  209. setSrc(img, genHierarchyImageSrc(img));
  210. for (var i = 0; i < this.children.length; ++i)
  211. this.children[i].domEntry.style.display = "";
  212. }
  213. }
  214. this.collapse = function()
  215. {
  216. var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
  217. if (img.pmState == pmMinus)
  218. {
  219. img.pmState = pmPlus;
  220. setSrc(img, genHierarchyImageSrc(img));
  221. for (var i = 0; i < this.children.length; ++i)
  222. this.children[i].domEntry.style.display = "none";
  223. }
  224. }
  225. this.toggle = function()
  226. {
  227. var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
  228. if (img.pmState == pmMinus)
  229. this.collapse();
  230. else
  231. this.expand();
  232. }
  233. this.select = function()
  234. {
  235. if (this.owner.selection != this)
  236. {
  237. if (this.owner.selection)
  238. this.owner.selection.setHighlight(hlNone);
  239. this.owner.selection = this;
  240. this.setHighlight(hlSelected);
  241. }
  242. }
  243. this.setHighlight = function(mode)
  244. {
  245. if (mode == hlNone)
  246. {
  247. this.textSpan.style.backgroundColor = "";
  248. this.textSpan.style.color = "";
  249. this.textSpan.style.border = "";
  250. }
  251. else if (mode == hlGrey)
  252. {
  253. this.textSpan.style.backgroundColor = "#aaaaaa";
  254. this.textSpan.style.color = "";
  255. this.textSpan.style.border = "";
  256. }
  257. else if (mode == hlSelected)
  258. {
  259. this.textSpan.style.backgroundColor = "3399cc";
  260. this.textSpan.style.color = "white";
  261. this.textSpan.style.border = "dotted 1px red";
  262. }
  263. }
  264. this.setOnclick = function(proc)
  265. {
  266. this.onclick = proc;
  267. }
  268. this.setRef = function(url)
  269. {
  270. if (this.anchor)
  271. this.anchor.href = url;
  272. }
  273. this.processPMClick = function(e)
  274. {
  275. this.toggle();
  276. // prevent this line selection, stop bubbling
  277. if (e)
  278. e.stopPropagation(); // Mozilla way
  279. if (window.event)
  280. window.event.cancelBubble = true; // IE way
  281. }
  282. this.processOnclick = function()
  283. {
  284. this.select();
  285. if (this.onclick instanceof Function)
  286. this.onclick();
  287. }
  288. ///////////////////////////////////////////////////////////////////////////
  289. if (iconSrc)
  290. this.icon.src = iconSrc;
  291. else
  292. {
  293. this.icon.width = 0;
  294. this.icon.height = 0;
  295. }
  296. this.icon.style.verticalAlign = "middle";
  297. this.icon.style.position = "relative";
  298. this.icon.style.top = "-1px";
  299. this.icon.style.paddingRight = "2px";
  300. if (!hrefMode)
  301. {
  302. this.textSpan.appendChild( this.textElement );
  303. }
  304. else
  305. {
  306. this.anchor = document.createElement("a");
  307. this.anchor.appendChild( this.textElement );
  308. this.textSpan.appendChild( this.anchor );
  309. }
  310. this.lineDiv.appendChild( this.icon );
  311. this.lineDiv.appendChild( this.textSpan );
  312. this.domEntry.appendChild( this.lineDiv );
  313. this.lineDiv.owner = this;
  314. if (!hrefMode)
  315. this.lineDiv.onclick = new Function("this.owner.processOnclick();");
  316. }