unageanu-jiji 1.1.4 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (233) hide show
  1. data/ChangeLog +24 -1
  2. data/README +0 -0
  3. data/base/shared_lib/moving_average.rb +34 -33
  4. data/base/shared_lib/system/cross.rb +76 -0
  5. data/base/shared_lib/system/position_manager.rb +160 -0
  6. data/base/shared_lib/system/signal.rb +347 -0
  7. data/html/css/datatable.css +4 -4
  8. data/html/css/default.css +57 -5
  9. data/html/css/treeview.css +205 -0
  10. data/html/img/bin_closed.png +0 -0
  11. data/html/img/bin_empty.png +0 -0
  12. data/html/img/button_add_small.gif +0 -0
  13. data/html/img/button_add_small_gray.gif +0 -0
  14. data/html/img/button_add_small_over.gif +0 -0
  15. data/html/img/button_mkcol.gif +0 -0
  16. data/html/img/button_mkcol_gray.gif +0 -0
  17. data/html/img/button_mkcol_over.gif +0 -0
  18. data/html/img/button_remove_small.gif +0 -0
  19. data/html/img/button_remove_small_gray.gif +0 -0
  20. data/html/img/button_remove_small_over.gif +0 -0
  21. data/html/img/button_rename.gif +0 -0
  22. data/html/img/button_rename_gray.gif +0 -0
  23. data/html/img/button_rename_over.gif +0 -0
  24. data/html/img/control_play.png +0 -0
  25. data/html/img/control_play_blue.png +0 -0
  26. data/html/img/folder.png +0 -0
  27. data/html/img/folder_brick.png +0 -0
  28. data/html/img/folder_user.png +0 -0
  29. data/html/{js/codepress/images → img}/line-numbers.png +0 -0
  30. data/html/img/page_white_ruby.png +0 -0
  31. data/html/img/sidebar_agent_edit.png +0 -0
  32. data/html/img/sidebar_agent_edit_over.png +0 -0
  33. data/html/img/sidebar_agent_edit_s.png +0 -0
  34. data/html/img/yui/treeview-sprite.gif +0 -0
  35. data/html/index.html +34 -18
  36. data/html/js/agent-editor/agent-editor-page.js +324 -0
  37. data/html/js/agent-editor/agent-editor.js +363 -0
  38. data/html/js/agent-editor/agent-list-tree.js +251 -0
  39. data/html/js/agent-selector.js +23 -28
  40. data/html/js/app.js +63 -91
  41. data/html/js/bt-create-page.js +25 -19
  42. data/html/js/container-min.js +1 -1
  43. data/html/js/container.js +944 -0
  44. data/html/js/edit_area/autocompletion.js +11 -13
  45. data/html/js/edit_area/edit_area.css +79 -40
  46. data/html/js/edit_area/edit_area.js +255 -226
  47. data/html/js/edit_area/edit_area_compressor.php +4 -4
  48. data/html/js/edit_area/edit_area_full.gz +0 -0
  49. data/html/js/edit_area/edit_area_full.js +31 -31
  50. data/html/js/edit_area/edit_area_full_with_plugins.gz +0 -0
  51. data/html/js/edit_area/edit_area_full_with_plugins.js +31 -31
  52. data/html/js/edit_area/edit_area_functions.js +448 -341
  53. data/html/js/edit_area/edit_area_loader.js +409 -397
  54. data/html/js/edit_area/elements_functions.js +120 -123
  55. data/html/js/edit_area/highlight.js +305 -197
  56. data/html/js/edit_area/images/goto.png +0 -0
  57. data/html/js/edit_area/images/help.png +0 -0
  58. data/html/js/edit_area/images/redo.png +0 -0
  59. data/html/js/edit_area/images/save.png +0 -0
  60. data/html/js/edit_area/images/search.png +0 -0
  61. data/html/js/edit_area/images/undo.png +0 -0
  62. data/html/js/edit_area/images/word_wrap.gif +0 -0
  63. data/html/js/edit_area/keyboard.js +5 -5
  64. data/html/js/edit_area/langs/bg.js +73 -0
  65. data/html/js/edit_area/langs/cs.js +2 -0
  66. data/html/js/edit_area/langs/de.js +2 -0
  67. data/html/js/edit_area/langs/dk.js +2 -0
  68. data/html/js/edit_area/langs/en.js +2 -0
  69. data/html/js/edit_area/langs/eo.js +2 -0
  70. data/html/js/edit_area/langs/es.js +2 -0
  71. data/html/js/edit_area/langs/fi.js +67 -0
  72. data/html/js/edit_area/langs/fr.js +2 -0
  73. data/html/js/edit_area/langs/hr.js +2 -0
  74. data/html/js/edit_area/langs/it.js +2 -0
  75. data/html/js/edit_area/langs/ja.js +2 -0
  76. data/html/js/edit_area/langs/mk.js +2 -0
  77. data/html/js/edit_area/langs/nl.js +2 -0
  78. data/html/js/edit_area/langs/pl.js +2 -0
  79. data/html/js/edit_area/langs/pt.js +2 -0
  80. data/html/js/edit_area/langs/ru.js +2 -0
  81. data/html/js/edit_area/langs/sk.js +2 -0
  82. data/html/js/edit_area/langs/zh.js +67 -0
  83. data/html/js/edit_area/manage_area.js +362 -205
  84. data/html/js/edit_area/plugins/charmap/langs/bg.js +12 -0
  85. data/html/js/edit_area/plugins/charmap/langs/zh.js +6 -0
  86. data/html/js/edit_area/plugins/test/langs/bg.js +10 -0
  87. data/html/js/edit_area/plugins/test/langs/zh.js +4 -0
  88. data/html/js/edit_area/reg_syntax/java.js +56 -0
  89. data/html/js/edit_area/reg_syntax/ruby.js +9 -9
  90. data/html/js/edit_area/reg_syntax.js +15 -13
  91. data/html/js/edit_area/regexp.js +36 -32
  92. data/html/js/edit_area/resize_area.js +43 -47
  93. data/html/js/edit_area/search_replace.js +29 -29
  94. data/html/js/edit_area/template.html +6 -4
  95. data/html/js/json-broker-client.js +23 -17
  96. data/html/js/result-page.js +107 -57
  97. data/html/js/rt-setting-page.js +38 -15
  98. data/html/js/sidebar.js +41 -27
  99. data/html/js/templates.js +167 -32
  100. data/html/js/utils.js +143 -7
  101. data/html/js/yui/treeview.js +3671 -0
  102. data/html/swf/chart.swf +0 -0
  103. data/html/test/agent_editor_spec.js +815 -0
  104. data/html/test/index.html +40 -0
  105. data/html/test/jsspec/JSSpec.css +224 -0
  106. data/html/test/jsspec/JSSpec.js +1549 -0
  107. data/html/test/jsspec/diff_match_patch.js +1 -0
  108. data/html/test/utils_spec.js +111 -0
  109. data/lib/jiji/agent/agent.rb +69 -12
  110. data/lib/jiji/agent/agent_manager.rb +18 -12
  111. data/lib/jiji/agent/agent_registry.rb +35 -121
  112. data/lib/jiji/collector.rb +16 -6
  113. data/lib/jiji/command.rb +46 -5
  114. data/lib/jiji/dao/file_system_dao.rb +158 -0
  115. data/lib/jiji/dao/timed_data_dao.rb +2 -0
  116. data/lib/jiji/dao/trade_result_dao.rb +1 -1
  117. data/lib/jiji/error.rb +24 -8
  118. data/lib/jiji/migration/migrator1_2_0.rb +67 -0
  119. data/lib/jiji/models.rb +82 -24
  120. data/lib/jiji/operator.rb +55 -51
  121. data/lib/jiji/output.rb +85 -29
  122. data/lib/jiji/output_manager.rb +84 -0
  123. data/lib/jiji/plugin/embedded/single_click_client.rb +2 -2
  124. data/lib/jiji/plugin/securities_plugin.rb +0 -1
  125. data/lib/jiji/process.rb +229 -208
  126. data/lib/jiji/process_manager.rb +190 -96
  127. data/lib/jiji/registry.rb +87 -19
  128. data/lib/jiji/server.rb +1 -0
  129. data/lib/jiji/service/agent_service.rb +147 -48
  130. data/lib/jiji/service/output_service.rb +37 -17
  131. data/lib/jiji/service/process_service.rb +3 -5
  132. data/lib/jiji/service/trade_result_service.rb +4 -5
  133. data/lib/jiji/util/file_lock.rb +4 -4
  134. data/lib/jiji/util/include_proxy.rb +17 -0
  135. data/lib/jiji/util/json_broker.rb +6 -4
  136. data/lib/jiji/util/util.rb +1 -1
  137. data/swf/chart/fx/chart/Chart.as +7 -0
  138. data/swf/chart/fx/chart/ui/graph/GraphManager.as +15 -2
  139. data/test/ProcessTest/agents/foo.rb +10 -0
  140. data/test/ProcessTest/conf/configuration.yaml +3 -0
  141. data/test/agent/agent_tests.rb +10 -0
  142. data/test/agent/test_AgentManager.rb +28 -12
  143. data/test/agent/test_AgentRegistry.rb +194 -99
  144. data/test/agent/test_PeriodicallyAgent.rb +1 -2
  145. data/test/agent/test_Permitter.rb +1 -2
  146. data/test/all_tests.rb +7 -19
  147. data/test/dao/dao_tests.rb +9 -0
  148. data/test/dao/test_FileSystemDao.rb +431 -0
  149. data/test/dao/test_RateDao.rb +5 -7
  150. data/test/dao/test_TradeResultDao.rb +1 -2
  151. data/test/migration/migration_tests.rb +10 -0
  152. data/test/migration/migrator1_2_0test_data/basic/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  153. data/test/migration/migrator1_2_0test_data/basic/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  154. data/test/migration/migrator1_2_0test_data/basic/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  155. data/test/migration/migrator1_2_0test_data/basic/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
  156. data/test/migration/migrator1_2_0test_data/basic/props.yaml +85 -0
  157. data/test/migration/migrator1_2_0test_data/illegal_props/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  158. data/test/migration/migrator1_2_0test_data/illegal_props/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  159. data/test/migration/migrator1_2_0test_data/illegal_props/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  160. data/test/migration/migrator1_2_0test_data/illegal_props/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
  161. data/test/migration/migrator1_2_0test_data/illegal_props/props.yaml +1 -0
  162. data/test/migration/migrator1_2_0test_data/no_outs/props.yaml +85 -0
  163. data/test/migration/migrator1_2_0test_data/no_props/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  164. data/test/migration/migrator1_2_0test_data/no_props/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  165. data/test/migration/migrator1_2_0test_data/no_props/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
  166. data/test/migration/migrator1_2_0test_data/no_props/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
  167. data/test/migration/test_Migrator.rb +1 -1
  168. data/test/migration/test_Migrator1_0_3.rb +1 -1
  169. data/test/migration/test_Migrator1_1_0.rb +1 -1
  170. data/test/migration/test_Migrator1_2_0.rb +94 -0
  171. data/test/plugin/embedded/test_SingleClickClient.rb +1 -2
  172. data/test/plugin/plugin_tests.rb +8 -0
  173. data/test/plugin/test_Loader.rb +1 -1
  174. data/test/shared/rate.csv +144 -0
  175. data/test/shared/shared_tests.rb +9 -0
  176. data/test/shared/test_Cross.rb +144 -0
  177. data/test/shared/test_PositionManager.rb +285 -0
  178. data/test/shared/test_Signal.rb +65 -0
  179. data/test/test_Output.rb +28 -21
  180. data/test/test_OutputManager.rb +162 -0
  181. data/test/test_Output_registry.rb +6 -17
  182. data/test/test_Process.rb +434 -222
  183. data/test/test_ProcessManager.rb +458 -101
  184. data/test/test_utils.rb +71 -8
  185. data/test/util/test_BlockToSession.rb +1 -2
  186. data/test/util/test_CSV.rb +1 -2
  187. data/test/util/test_SynchronizeInterceptor.rb +1 -2
  188. data/test/util/util_tests.rb +9 -0
  189. metadata +127 -47
  190. data/html/js/agent-editor-page.js +0 -440
  191. data/html/js/codepress/codepress.css +0 -21
  192. data/html/js/codepress/codepress.html +0 -35
  193. data/html/js/codepress/codepress.js +0 -138
  194. data/html/js/codepress/engines/gecko.js +0 -293
  195. data/html/js/codepress/engines/khtml.js +0 -0
  196. data/html/js/codepress/engines/msie.js +0 -304
  197. data/html/js/codepress/engines/older.js +0 -0
  198. data/html/js/codepress/engines/opera.js +0 -260
  199. data/html/js/codepress/images/line-numbers.gif +0 -0
  200. data/html/js/codepress/index.html +0 -443
  201. data/html/js/codepress/languages/asp.css +0 -71
  202. data/html/js/codepress/languages/asp.js +0 -117
  203. data/html/js/codepress/languages/autoit.css +0 -13
  204. data/html/js/codepress/languages/autoit.js +0 -32
  205. data/html/js/codepress/languages/csharp.css +0 -9
  206. data/html/js/codepress/languages/csharp.js +0 -25
  207. data/html/js/codepress/languages/css.css +0 -10
  208. data/html/js/codepress/languages/css.js +0 -23
  209. data/html/js/codepress/languages/generic.css +0 -9
  210. data/html/js/codepress/languages/generic.js +0 -25
  211. data/html/js/codepress/languages/html.css +0 -13
  212. data/html/js/codepress/languages/html.js +0 -59
  213. data/html/js/codepress/languages/java.css +0 -7
  214. data/html/js/codepress/languages/java.js +0 -24
  215. data/html/js/codepress/languages/javascript.css +0 -8
  216. data/html/js/codepress/languages/javascript.js +0 -30
  217. data/html/js/codepress/languages/perl.css +0 -11
  218. data/html/js/codepress/languages/perl.js +0 -27
  219. data/html/js/codepress/languages/php.css +0 -12
  220. data/html/js/codepress/languages/php.js +0 -61
  221. data/html/js/codepress/languages/ruby.css +0 -10
  222. data/html/js/codepress/languages/ruby.js +0 -26
  223. data/html/js/codepress/languages/sql.css +0 -10
  224. data/html/js/codepress/languages/sql.js +0 -30
  225. data/html/js/codepress/languages/text.css +0 -5
  226. data/html/js/codepress/languages/text.js +0 -9
  227. data/html/js/codepress/languages/vbscript.css +0 -71
  228. data/html/js/codepress/languages/vbscript.js +0 -117
  229. data/html/js/codepress/languages/xsl.css +0 -15
  230. data/html/js/codepress/languages/xsl.js +0 -103
  231. data/html/js/codepress/license.txt +0 -458
  232. data/html/js/edit_area/images/Thumbs.db +0 -0
  233. data/test/plugin/test_gem/a/lib/jiji_plugin_test.rb +0 -6
@@ -0,0 +1,3671 @@
1
+ /*
2
+ Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
+ Code licensed under the BSD License:
4
+ http://developer.yahoo.net/yui/license.txt
5
+ version: 2.7.0
6
+ */
7
+ (function () {
8
+ var Dom = YAHOO.util.Dom,
9
+ Event = YAHOO.util.Event,
10
+ Lang = YAHOO.lang,
11
+ Widget = YAHOO.widget;
12
+
13
+
14
+
15
+ /**
16
+ * The treeview widget is a generic tree building tool.
17
+ * @module treeview
18
+ * @title TreeView Widget
19
+ * @requires yahoo, event
20
+ * @optional animation, json
21
+ * @namespace YAHOO.widget
22
+ */
23
+
24
+ /**
25
+ * Contains the tree view state data and the root node.
26
+ *
27
+ * @class TreeView
28
+ * @uses YAHOO.util.EventProvider
29
+ * @constructor
30
+ * @param {string|HTMLElement} id The id of the element, or the element itself that the tree will be inserted into. Existing markup in this element, if valid, will be used to build the tree
31
+ * @param {Array|object|string} oConfig (optional) An array containing the definition of the tree. (see buildTreeFromObject)
32
+ *
33
+ */
34
+ YAHOO.widget.TreeView = function(id, oConfig) {
35
+ if (id) { this.init(id); }
36
+ if (oConfig) {
37
+ if (!Lang.isArray(oConfig)) {
38
+ oConfig = [oConfig];
39
+ }
40
+ this.buildTreeFromObject(oConfig);
41
+ } else if (Lang.trim(this._el.innerHTML)) {
42
+ this.buildTreeFromMarkup(id);
43
+ }
44
+ };
45
+
46
+ var TV = Widget.TreeView;
47
+
48
+ TV.prototype = {
49
+
50
+ /**
51
+ * The id of tree container element
52
+ * @property id
53
+ * @type String
54
+ */
55
+ id: null,
56
+
57
+ /**
58
+ * The host element for this tree
59
+ * @property _el
60
+ * @private
61
+ * @type HTMLelement
62
+ */
63
+ _el: null,
64
+
65
+ /**
66
+ * Flat collection of all nodes in this tree. This is a sparse
67
+ * array, so the length property can't be relied upon for a
68
+ * node count for the tree.
69
+ * @property _nodes
70
+ * @type Node[]
71
+ * @private
72
+ */
73
+ _nodes: null,
74
+
75
+ /**
76
+ * We lock the tree control while waiting for the dynamic loader to return
77
+ * @property locked
78
+ * @type boolean
79
+ */
80
+ locked: false,
81
+
82
+ /**
83
+ * The animation to use for expanding children, if any
84
+ * @property _expandAnim
85
+ * @type string
86
+ * @private
87
+ */
88
+ _expandAnim: null,
89
+
90
+ /**
91
+ * The animation to use for collapsing children, if any
92
+ * @property _collapseAnim
93
+ * @type string
94
+ * @private
95
+ */
96
+ _collapseAnim: null,
97
+
98
+ /**
99
+ * The current number of animations that are executing
100
+ * @property _animCount
101
+ * @type int
102
+ * @private
103
+ */
104
+ _animCount: 0,
105
+
106
+ /**
107
+ * The maximum number of animations to run at one time.
108
+ * @property maxAnim
109
+ * @type int
110
+ */
111
+ maxAnim: 2,
112
+
113
+ /**
114
+ * Whether there is any subscriber to dblClickEvent
115
+ * @property _hasDblClickSubscriber
116
+ * @type boolean
117
+ * @private
118
+ */
119
+ _hasDblClickSubscriber: false,
120
+
121
+ /**
122
+ * Stores the timer used to check for double clicks
123
+ * @property _dblClickTimer
124
+ * @type window.timer object
125
+ * @private
126
+ */
127
+ _dblClickTimer: null,
128
+
129
+ /**
130
+ * A reference to the Node currently having the focus or null if none.
131
+ * @property currentFocus
132
+ * @type YAHOO.widget.Node
133
+ */
134
+ currentFocus: null,
135
+
136
+ /**
137
+ * If true, only one Node can be highlighted at a time
138
+ * @property singleNodeHighlight
139
+ * @type boolean
140
+ * @default false
141
+ */
142
+
143
+ singleNodeHighlight: false,
144
+
145
+ /**
146
+ * A reference to the Node that is currently highlighted.
147
+ * It is only meaningful if singleNodeHighlight is enabled
148
+ * @property _currentlyHighlighted
149
+ * @type YAHOO.widget.Node
150
+ * @default null
151
+ * @private
152
+ */
153
+
154
+ _currentlyHighlighted: null,
155
+
156
+ /**
157
+ * Sets up the animation for expanding children
158
+ * @method setExpandAnim
159
+ * @param {string} type the type of animation (acceptable values defined
160
+ * in YAHOO.widget.TVAnim)
161
+ */
162
+ setExpandAnim: function(type) {
163
+ this._expandAnim = (Widget.TVAnim.isValid(type)) ? type : null;
164
+ },
165
+
166
+ /**
167
+ * Sets up the animation for collapsing children
168
+ * @method setCollapseAnim
169
+ * @param {string} the type of animation (acceptable values defined in
170
+ * YAHOO.widget.TVAnim)
171
+ */
172
+ setCollapseAnim: function(type) {
173
+ this._collapseAnim = (Widget.TVAnim.isValid(type)) ? type : null;
174
+ },
175
+
176
+ /**
177
+ * Perform the expand animation if configured, or just show the
178
+ * element if not configured or too many animations are in progress
179
+ * @method animateExpand
180
+ * @param el {HTMLElement} the element to animate
181
+ * @param node {YAHOO.util.Node} the node that was expanded
182
+ * @return {boolean} true if animation could be invoked, false otherwise
183
+ */
184
+ animateExpand: function(el, node) {
185
+
186
+ if (this._expandAnim && this._animCount < this.maxAnim) {
187
+ // this.locked = true;
188
+ var tree = this;
189
+ var a = Widget.TVAnim.getAnim(this._expandAnim, el,
190
+ function() { tree.expandComplete(node); });
191
+ if (a) {
192
+ ++this._animCount;
193
+ this.fireEvent("animStart", {
194
+ "node": node,
195
+ "type": "expand"
196
+ });
197
+ a.animate();
198
+ }
199
+
200
+ return true;
201
+ }
202
+
203
+ return false;
204
+ },
205
+
206
+ /**
207
+ * Perform the collapse animation if configured, or just show the
208
+ * element if not configured or too many animations are in progress
209
+ * @method animateCollapse
210
+ * @param el {HTMLElement} the element to animate
211
+ * @param node {YAHOO.util.Node} the node that was expanded
212
+ * @return {boolean} true if animation could be invoked, false otherwise
213
+ */
214
+ animateCollapse: function(el, node) {
215
+
216
+ if (this._collapseAnim && this._animCount < this.maxAnim) {
217
+ // this.locked = true;
218
+ var tree = this;
219
+ var a = Widget.TVAnim.getAnim(this._collapseAnim, el,
220
+ function() { tree.collapseComplete(node); });
221
+ if (a) {
222
+ ++this._animCount;
223
+ this.fireEvent("animStart", {
224
+ "node": node,
225
+ "type": "collapse"
226
+ });
227
+ a.animate();
228
+ }
229
+
230
+ return true;
231
+ }
232
+
233
+ return false;
234
+ },
235
+
236
+ /**
237
+ * Function executed when the expand animation completes
238
+ * @method expandComplete
239
+ */
240
+ expandComplete: function(node) {
241
+ --this._animCount;
242
+ this.fireEvent("animComplete", {
243
+ "node": node,
244
+ "type": "expand"
245
+ });
246
+ // this.locked = false;
247
+ },
248
+
249
+ /**
250
+ * Function executed when the collapse animation completes
251
+ * @method collapseComplete
252
+ */
253
+ collapseComplete: function(node) {
254
+ --this._animCount;
255
+ this.fireEvent("animComplete", {
256
+ "node": node,
257
+ "type": "collapse"
258
+ });
259
+ // this.locked = false;
260
+ },
261
+
262
+ /**
263
+ * Initializes the tree
264
+ * @method init
265
+ * @parm {string|HTMLElement} id the id of the element that will hold the tree
266
+ * @private
267
+ */
268
+ init: function(id) {
269
+ this._el = Dom.get(id);
270
+ this.id = Dom.generateId(this._el,"yui-tv-auto-id-");
271
+
272
+ /**
273
+ * When animation is enabled, this event fires when the animation
274
+ * starts
275
+ * @event animStart
276
+ * @type CustomEvent
277
+ * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
278
+ * @parm {String} type the type of animation ("expand" or "collapse")
279
+ */
280
+ this.createEvent("animStart", this);
281
+
282
+ /**
283
+ * When animation is enabled, this event fires when the animation
284
+ * completes
285
+ * @event animComplete
286
+ * @type CustomEvent
287
+ * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
288
+ * @parm {String} type the type of animation ("expand" or "collapse")
289
+ */
290
+ this.createEvent("animComplete", this);
291
+
292
+ /**
293
+ * Fires when a node is going to be collapsed. Return false to stop
294
+ * the collapse.
295
+ * @event collapse
296
+ * @type CustomEvent
297
+ * @param {YAHOO.widget.Node} node the node that is collapsing
298
+ */
299
+ this.createEvent("collapse", this);
300
+
301
+ /**
302
+ * Fires after a node is successfully collapsed. This event will not fire
303
+ * if the "collapse" event was cancelled.
304
+ * @event collapseComplete
305
+ * @type CustomEvent
306
+ * @param {YAHOO.widget.Node} node the node that was collapsed
307
+ */
308
+ this.createEvent("collapseComplete", this);
309
+
310
+ /**
311
+ * Fires when a node is going to be expanded. Return false to stop
312
+ * the collapse.
313
+ * @event expand
314
+ * @type CustomEvent
315
+ * @param {YAHOO.widget.Node} node the node that is expanding
316
+ */
317
+ this.createEvent("expand", this);
318
+
319
+ /**
320
+ * Fires after a node is successfully expanded. This event will not fire
321
+ * if the "expand" event was cancelled.
322
+ * @event expandComplete
323
+ * @type CustomEvent
324
+ * @param {YAHOO.widget.Node} node the node that was expanded
325
+ */
326
+ this.createEvent("expandComplete", this);
327
+
328
+ /**
329
+ * Fires when the Enter key is pressed on a node that has the focus
330
+ * @event enterKeyPressed
331
+ * @type CustomEvent
332
+ * @param {YAHOO.widget.Node} node the node that has the focus
333
+ */
334
+ this.createEvent("enterKeyPressed", this);
335
+
336
+ /**
337
+ * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a Click.
338
+ * The listener may return false to cancel toggling and focusing on the node.
339
+ * @event clickEvent
340
+ * @type CustomEvent
341
+ * @param oArgs.event {HTMLEvent} The event object
342
+ * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked
343
+ */
344
+ this.createEvent("clickEvent", this);
345
+
346
+ /**
347
+ * Fires when the focus receives the focus, when it changes from a Node
348
+ * to another Node or when it is completely lost (blurred)
349
+ * @event focusChanged
350
+ * @type CustomEvent
351
+ * @param oArgs.oldNode {YAHOO.widget.Node} Node that had the focus or null if none
352
+ * @param oArgs.newNode {YAHOO.widget.Node} Node that receives the focus or null if none
353
+ */
354
+
355
+ this.createEvent('focusChanged',this);
356
+
357
+ /**
358
+ * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a double Click
359
+ * @event dblClickEvent
360
+ * @type CustomEvent
361
+ * @param oArgs.event {HTMLEvent} The event object
362
+ * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked
363
+ */
364
+ var self = this;
365
+ this.createEvent("dblClickEvent", {
366
+ scope:this,
367
+ onSubscribeCallback: function() {
368
+ self._hasDblClickSubscriber = true;
369
+ }
370
+ });
371
+
372
+ /**
373
+ * Custom event that is fired when the text node label is clicked.
374
+ * The node clicked is provided as an argument
375
+ *
376
+ * @event labelClick
377
+ * @type CustomEvent
378
+ * @param {YAHOO.widget.Node} node the node clicked
379
+ * @deprecated use clickEvent or dblClickEvent
380
+ */
381
+ this.createEvent("labelClick", this);
382
+
383
+ /**
384
+ * Custom event fired when the highlight of a node changes.
385
+ * The node that triggered the change is provided as an argument:
386
+ * The status of the highlight can be checked in
387
+ * <a href="YAHOO.widget.Node.html#property_highlightState">nodeRef.highlightState</a>.
388
+ * Depending on <a href="YAHOO.widget.Node.html#property_propagateHighlight">nodeRef.propagateHighlight</a>, other nodes might have changed
389
+ * @event highlightEvent
390
+ * @type CustomEvent
391
+ * @param node{YAHOO.widget.Node} the node that started the change in highlighting state
392
+ */
393
+ this.createEvent("highlightEvent",this);
394
+
395
+
396
+
397
+ this._nodes = [];
398
+
399
+ // store a global reference
400
+ TV.trees[this.id] = this;
401
+
402
+ // Set up the root node
403
+ this.root = new Widget.RootNode(this);
404
+
405
+ var LW = Widget.LogWriter;
406
+
407
+
408
+
409
+ // YAHOO.util.Event.onContentReady(this.id, this.handleAvailable, this, true);
410
+ // YAHOO.util.Event.on(this.id, "click", this.handleClick, this, true);
411
+ },
412
+
413
+ //handleAvailable: function() {
414
+ //var Event = YAHOO.util.Event;
415
+ //Event.on(this.id,
416
+ //},
417
+ /**
418
+ * Builds the TreeView from an object.
419
+ * This is the method called by the constructor to build the tree when it has a second argument.
420
+ * A tree can be described by an array of objects, each object corresponding to a node.
421
+ * Node descriptions may contain values for any property of a node plus the following extra properties: <ul>
422
+ * <li>type: can be one of the following:<ul>
423
+ * <li> A shortname for a node type (<code>'text','menu','html'</code>) </li>
424
+ * <li>The name of a Node class under YAHOO.widget (<code>'TextNode', 'MenuNode', 'DateNode'</code>, etc) </li>
425
+ * <li>a reference to an actual class: <code>YAHOO.widget.DateNode</code></li></ul></li>
426
+ * <li>children: an array containing further node definitions</li></ul>
427
+ * @method buildTreeFromObject
428
+ * @param oConfig {Array} array containing a full description of the tree
429
+ *
430
+ */
431
+ buildTreeFromObject: function (oConfig) {
432
+ var build = function (parent, oConfig) {
433
+ var i, item, node, children, type, NodeType, ThisType;
434
+ for (i = 0; i < oConfig.length; i++) {
435
+ item = oConfig[i];
436
+ if (Lang.isString(item)) {
437
+ node = new Widget.TextNode(item, parent);
438
+ } else if (Lang.isObject(item)) {
439
+ children = item.children;
440
+ delete item.children;
441
+ type = item.type || 'text';
442
+ delete item.type;
443
+ switch (Lang.isString(type) && type.toLowerCase()) {
444
+ case 'text':
445
+ node = new Widget.TextNode(item, parent);
446
+ break;
447
+ case 'menu':
448
+ node = new Widget.MenuNode(item, parent);
449
+ break;
450
+ case 'html':
451
+ node = new Widget.HTMLNode(item, parent);
452
+ break;
453
+ default:
454
+ if (Lang.isString(type)) {
455
+ NodeType = Widget[type];
456
+ } else {
457
+ NodeType = type;
458
+ }
459
+ if (Lang.isObject(NodeType)) {
460
+ for (ThisType = NodeType; ThisType && ThisType !== Widget.Node; ThisType = ThisType.superclass.constructor) {}
461
+ if (ThisType) {
462
+ node = new NodeType(item, parent);
463
+ } else {
464
+ }
465
+ } else {
466
+ }
467
+ }
468
+ if (children) {
469
+ build(node,children);
470
+ }
471
+ } else {
472
+ }
473
+ }
474
+ };
475
+
476
+
477
+ build(this.root,oConfig);
478
+ },
479
+ /**
480
+ * Builds the TreeView from existing markup. Markup should consist of &lt;UL&gt; or &lt;OL&gt; elements containing &lt;LI&gt; elements.
481
+ * Each &lt;LI&gt; can have one element used as label and a second optional element which is to be a &lt;UL&gt; or &lt;OL&gt;
482
+ * containing nested nodes.
483
+ * Depending on what the first element of the &lt;LI&gt; element is, the following Nodes will be created: <ul>
484
+ * <li>plain text: a regular TextNode</li>
485
+ * <li>anchor &lt;A&gt;: a TextNode with its <code>href</code> and <code>target</code> taken from the anchor</li>
486
+ * <li>anything else: an HTMLNode</li></ul>
487
+ * Only the first outermost (un-)ordered list in the markup and its children will be parsed.
488
+ * Nodes will be collapsed unless an &lt;LI&gt; tag has a className called 'expanded'.
489
+ * All other className attributes will be copied over to the Node className property.
490
+ * If the &lt;LI&gt; element contains an attribute called <code>yuiConfig</code>, its contents should be a JSON-encoded object
491
+ * as the one used in method <a href="#method_buildTreeFromObject">buildTreeFromObject</a>.
492
+ * @method buildTreeFromMarkup
493
+ * @param id{string|HTMLElement} The id of the element that contains the markup or a reference to it.
494
+ */
495
+ buildTreeFromMarkup: function (id) {
496
+ var build = function (markup) {
497
+ var el, child, branch = [], config = {}, label, yuiConfig;
498
+ // Dom's getFirstChild and getNextSibling skip over text elements
499
+ for (el = Dom.getFirstChild(markup); el; el = Dom.getNextSibling(el)) {
500
+ switch (el.tagName.toUpperCase()) {
501
+ case 'LI':
502
+ label = '';
503
+ config = {
504
+ expanded: Dom.hasClass(el,'expanded'),
505
+ title: el.title || el.alt || null,
506
+ className: Lang.trim(el.className.replace(/\bexpanded\b/,'')) || null
507
+ };
508
+ // I cannot skip over text elements here because I want them for labels
509
+ child = el.firstChild;
510
+ if (child.nodeType == 3) {
511
+ // nodes with only whitespace, tabs and new lines don't count, they are probably just formatting.
512
+ label = Lang.trim(child.nodeValue.replace(/[\n\t\r]*/g,''));
513
+ if (label) {
514
+ config.type = 'text';
515
+ config.label = label;
516
+ } else {
517
+ child = Dom.getNextSibling(child);
518
+ }
519
+ }
520
+ if (!label) {
521
+ if (child.tagName.toUpperCase() == 'A') {
522
+ config.type = 'text';
523
+ config.label = child.innerHTML;
524
+ config.href = child.href;
525
+ config.target = child.target;
526
+ config.title = child.title || child.alt || config.title;
527
+ } else {
528
+ config.type = 'html';
529
+ var d = document.createElement('div');
530
+ d.appendChild(child.cloneNode(true));
531
+ config.html = d.innerHTML;
532
+ config.hasIcon = true;
533
+ }
534
+ }
535
+ // see if after the label it has a further list which will become children of this node.
536
+ child = Dom.getNextSibling(child);
537
+ switch (child && child.tagName.toUpperCase()) {
538
+ case 'UL':
539
+ case 'OL':
540
+ config.children = build(child);
541
+ break;
542
+ }
543
+ // if there are further elements or text, it will be ignored.
544
+
545
+ if (YAHOO.lang.JSON) {
546
+ yuiConfig = el.getAttribute('yuiConfig');
547
+ if (yuiConfig) {
548
+ yuiConfig = YAHOO.lang.JSON.parse(yuiConfig);
549
+ config = YAHOO.lang.merge(config,yuiConfig);
550
+ }
551
+ }
552
+
553
+ branch.push(config);
554
+ break;
555
+ case 'UL':
556
+ case 'OL':
557
+ config = {
558
+ type: 'text',
559
+ label: '',
560
+ children: build(child)
561
+ };
562
+ branch.push(config);
563
+ break;
564
+ }
565
+ }
566
+ return branch;
567
+ };
568
+
569
+ var markup = Dom.getChildrenBy(Dom.get(id),function (el) {
570
+ var tag = el.tagName.toUpperCase();
571
+ return tag == 'UL' || tag == 'OL';
572
+ });
573
+ if (markup.length) {
574
+ this.buildTreeFromObject(build(markup[0]));
575
+ } else {
576
+ }
577
+ },
578
+ /**
579
+ * Returns the TD element where the event has occurred
580
+ * @method _getEventTargetTdEl
581
+ * @private
582
+ */
583
+ _getEventTargetTdEl: function (ev) {
584
+ var target = Event.getTarget(ev);
585
+ // go up looking for a TD with a className with a ygtv prefix
586
+ while (target && !(target.tagName.toUpperCase() == 'TD' && Dom.hasClass(target.parentNode,'ygtvrow'))) {
587
+ target = Dom.getAncestorByTagName(target,'td');
588
+ }
589
+ if (Lang.isNull(target)) { return null; }
590
+ // If it is a spacer cell, do nothing
591
+ if (/\bygtv(blank)?depthcell/.test(target.className)) { return null;}
592
+ // If it has an id, search for the node number and see if it belongs to a node in this tree.
593
+ if (target.id) {
594
+ var m = target.id.match(/\bygtv([^\d]*)(.*)/);
595
+ if (m && m[2] && this._nodes[m[2]]) {
596
+ return target;
597
+ }
598
+ }
599
+ return null;
600
+ },
601
+ /**
602
+ * Event listener for click events
603
+ * @method _onClickEvent
604
+ * @private
605
+ */
606
+ _onClickEvent: function (ev) {
607
+ var self = this,
608
+ td = this._getEventTargetTdEl(ev),
609
+ node,
610
+ target,
611
+ toggle = function () {
612
+ node.toggle();
613
+ node.focus();
614
+ try {
615
+ Event.preventDefault(ev);
616
+ } catch (e) {
617
+ // @TODO
618
+ // For some reason IE8 is providing an event object with
619
+ // most of the fields missing, but only when clicking on
620
+ // the node's label, and only when working with inline
621
+ // editing. This generates a "Member not found" error
622
+ // in that browser. Determine if this is a browser
623
+ // bug, or a problem with this code. Already checked to
624
+ // see if the problem has to do with access the event
625
+ // in the outer scope, and that isn't the problem.
626
+ // Maybe the markup for inline editing is broken.
627
+ }
628
+ };
629
+
630
+ if (!td) {
631
+ return;
632
+ }
633
+
634
+ node = this.getNodeByElement(td);
635
+ if (!node) {
636
+ return;
637
+ }
638
+
639
+ // exception to handle deprecated event labelClick
640
+ // @TODO take another look at this deprecation. It is common for people to
641
+ // only be interested in the label click, so why make them have to test
642
+ // the node type to figure out whether the click was on the label?
643
+ target = Event.getTarget(ev);
644
+ if (Dom.hasClass(target, node.labelStyle) || Dom.getAncestorByClassName(target,node.labelStyle)) {
645
+ this.fireEvent('labelClick',node);
646
+ }
647
+
648
+ // If it is a toggle cell, toggle
649
+ var tmp = {};
650
+ for ( var i in ev ) tmp[i] = ev[i];
651
+ ev = tmp;
652
+
653
+ if (/\bygtv[tl][mp]h?h?/.test(td.className)) {
654
+ toggle();
655
+ } else {
656
+ if (this._dblClickTimer) {
657
+ window.clearTimeout(this._dblClickTimer);
658
+ this._dblClickTimer = null;
659
+ } else {
660
+ if (this._hasDblClickSubscriber) {
661
+ this._dblClickTimer = window.setTimeout(function () {
662
+ self._dblClickTimer = null;
663
+ if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) {
664
+ toggle();
665
+ }
666
+ }, 200);
667
+ } else {
668
+ if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) {
669
+ toggle();
670
+ }
671
+ }
672
+ }
673
+ }
674
+ },
675
+
676
+ /**
677
+ * Event listener for double-click events
678
+ * @method _onDblClickEvent
679
+ * @private
680
+ */
681
+ _onDblClickEvent: function (ev) {
682
+ if (!this._hasDblClickSubscriber) { return; }
683
+ var td = this._getEventTargetTdEl(ev);
684
+ if (!td) {return;}
685
+
686
+ if (!(/\bygtv[tl][mp]h?h?/.test(td.className))) {
687
+ this.fireEvent('dblClickEvent', {event:ev, node:this.getNodeByElement(td)});
688
+ if (this._dblClickTimer) {
689
+ window.clearTimeout(this._dblClickTimer);
690
+ this._dblClickTimer = null;
691
+ }
692
+ }
693
+ },
694
+ /**
695
+ * Event listener for mouse over events
696
+ * @method _onMouseOverEvent
697
+ * @private
698
+ */
699
+ _onMouseOverEvent:function (ev) {
700
+ var target;
701
+ if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) {
702
+ target.className = target.className.replace(/\bygtv([lt])([mp])\b/gi,'ygtv$1$2h');
703
+ }
704
+ },
705
+ /**
706
+ * Event listener for mouse out events
707
+ * @method _onMouseOutEvent
708
+ * @private
709
+ */
710
+ _onMouseOutEvent: function (ev) {
711
+ var target;
712
+ if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) {
713
+ target.className = target.className.replace(/\bygtv([lt])([mp])h\b/gi,'ygtv$1$2');
714
+ }
715
+ },
716
+ /**
717
+ * Event listener for key down events
718
+ * @method _onKeyDownEvent
719
+ * @private
720
+ */
721
+ _onKeyDownEvent: function (ev) {
722
+ var target = Event.getTarget(ev),
723
+ node = this.getNodeByElement(target),
724
+ newNode = node,
725
+ KEY = YAHOO.util.KeyListener.KEY;
726
+
727
+ switch(ev.keyCode) {
728
+ case KEY.UP:
729
+ do {
730
+ if (newNode.previousSibling) {
731
+ newNode = newNode.previousSibling;
732
+ } else {
733
+ newNode = newNode.parent;
734
+ }
735
+ } while (newNode && !newNode._canHaveFocus());
736
+ if (newNode) { newNode.focus(); }
737
+ Event.preventDefault(ev);
738
+ break;
739
+ case KEY.DOWN:
740
+ do {
741
+ if (newNode.nextSibling) {
742
+ newNode = newNode.nextSibling;
743
+ } else {
744
+ newNode.expand();
745
+ newNode = (newNode.children.length || null) && newNode.children[0];
746
+ }
747
+ } while (newNode && !newNode._canHaveFocus);
748
+ if (newNode) { newNode.focus();}
749
+ Event.preventDefault(ev);
750
+ break;
751
+ case KEY.LEFT:
752
+ do {
753
+ if (newNode.parent) {
754
+ newNode = newNode.parent;
755
+ } else {
756
+ newNode = newNode.previousSibling;
757
+ }
758
+ } while (newNode && !newNode._canHaveFocus());
759
+ if (newNode) { newNode.focus();}
760
+ Event.preventDefault(ev);
761
+ break;
762
+ case KEY.RIGHT:
763
+ do {
764
+ newNode.expand();
765
+ if (newNode.children.length) {
766
+ newNode = newNode.children[0];
767
+ } else {
768
+ newNode = newNode.nextSibling;
769
+ }
770
+ } while (newNode && !newNode._canHaveFocus());
771
+ if (newNode) { newNode.focus();}
772
+ Event.preventDefault(ev);
773
+ break;
774
+ case KEY.ENTER:
775
+ if (node.href) {
776
+ if (node.target) {
777
+ window.open(node.href,node.target);
778
+ } else {
779
+ window.location(node.href);
780
+ }
781
+ } else {
782
+ node.toggle();
783
+ }
784
+ this.fireEvent('enterKeyPressed',node);
785
+ Event.preventDefault(ev);
786
+ break;
787
+ case KEY.HOME:
788
+ newNode = this.getRoot();
789
+ if (newNode.children.length) {newNode = newNode.children[0];}
790
+ if (newNode._canHaveFocus()) { newNode.focus(); }
791
+ Event.preventDefault(ev);
792
+ break;
793
+ case KEY.END:
794
+ newNode = newNode.parent.children;
795
+ newNode = newNode[newNode.length -1];
796
+ if (newNode._canHaveFocus()) { newNode.focus(); }
797
+ Event.preventDefault(ev);
798
+ break;
799
+ // case KEY.PAGE_UP:
800
+ // break;
801
+ // case KEY.PAGE_DOWN:
802
+ // break;
803
+ case 107: // plus key
804
+ if (ev.shiftKey) {
805
+ node.parent.expandAll();
806
+ } else {
807
+ node.expand();
808
+ }
809
+ break;
810
+ case 109: // minus key
811
+ if (ev.shiftKey) {
812
+ node.parent.collapseAll();
813
+ } else {
814
+ node.collapse();
815
+ }
816
+ break;
817
+ default:
818
+ break;
819
+ }
820
+ },
821
+ /**
822
+ * Renders the tree boilerplate and visible nodes
823
+ * @method render
824
+ */
825
+ render: function() {
826
+ var html = this.root.getHtml(),
827
+ el = this.getEl();
828
+ el.innerHTML = html;
829
+ if (!this._hasEvents) {
830
+ Event.on(el, 'click', this._onClickEvent, this, true);
831
+ Event.on(el, 'dblclick', this._onDblClickEvent, this, true);
832
+ Event.on(el, 'mouseover', this._onMouseOverEvent, this, true);
833
+ Event.on(el, 'mouseout', this._onMouseOutEvent, this, true);
834
+ Event.on(el, 'keydown', this._onKeyDownEvent, this, true);
835
+ }
836
+ this._hasEvents = true;
837
+ },
838
+
839
+ /**
840
+ * Returns the tree's host element
841
+ * @method getEl
842
+ * @return {HTMLElement} the host element
843
+ */
844
+ getEl: function() {
845
+ if (! this._el) {
846
+ this._el = Dom.get(this.id);
847
+ }
848
+ return this._el;
849
+ },
850
+
851
+ /**
852
+ * Nodes register themselves with the tree instance when they are created.
853
+ * @method regNode
854
+ * @param node {Node} the node to register
855
+ * @private
856
+ */
857
+ regNode: function(node) {
858
+ this._nodes[node.index] = node;
859
+ },
860
+
861
+ /**
862
+ * Returns the root node of this tree
863
+ * @method getRoot
864
+ * @return {Node} the root node
865
+ */
866
+ getRoot: function() {
867
+ return this.root;
868
+ },
869
+
870
+ /**
871
+ * Configures this tree to dynamically load all child data
872
+ * @method setDynamicLoad
873
+ * @param {function} fnDataLoader the function that will be called to get the data
874
+ * @param iconMode {int} configures the icon that is displayed when a dynamic
875
+ * load node is expanded the first time without children. By default, the
876
+ * "collapse" icon will be used. If set to 1, the leaf node icon will be
877
+ * displayed.
878
+ */
879
+ setDynamicLoad: function(fnDataLoader, iconMode) {
880
+ this.root.setDynamicLoad(fnDataLoader, iconMode);
881
+ },
882
+
883
+ /**
884
+ * Expands all child nodes. Note: this conflicts with the "multiExpand"
885
+ * node property. If expand all is called in a tree with nodes that
886
+ * do not allow multiple siblings to be displayed, only the last sibling
887
+ * will be expanded.
888
+ * @method expandAll
889
+ */
890
+ expandAll: function() {
891
+ if (!this.locked) {
892
+ this.root.expandAll();
893
+ }
894
+ },
895
+
896
+ /**
897
+ * Collapses all expanded child nodes in the entire tree.
898
+ * @method collapseAll
899
+ */
900
+ collapseAll: function() {
901
+ if (!this.locked) {
902
+ this.root.collapseAll();
903
+ }
904
+ },
905
+
906
+ /**
907
+ * Returns a node in the tree that has the specified index (this index
908
+ * is created internally, so this function probably will only be used
909
+ * in html generated for a given node.)
910
+ * @method getNodeByIndex
911
+ * @param {int} nodeIndex the index of the node wanted
912
+ * @return {Node} the node with index=nodeIndex, null if no match
913
+ */
914
+ getNodeByIndex: function(nodeIndex) {
915
+ var n = this._nodes[nodeIndex];
916
+ return (n) ? n : null;
917
+ },
918
+
919
+ /**
920
+ * Returns a node that has a matching property and value in the data
921
+ * object that was passed into its constructor.
922
+ * @method getNodeByProperty
923
+ * @param {object} property the property to search (usually a string)
924
+ * @param {object} value the value we want to find (usuall an int or string)
925
+ * @return {Node} the matching node, null if no match
926
+ */
927
+ getNodeByProperty: function(property, value) {
928
+ for (var i in this._nodes) {
929
+ if (this._nodes.hasOwnProperty(i)) {
930
+ var n = this._nodes[i];
931
+ if ((property in n && n[property] == value) || (n.data && value == n.data[property])) {
932
+ return n;
933
+ }
934
+ }
935
+ }
936
+
937
+ return null;
938
+ },
939
+
940
+ /**
941
+ * Returns a collection of nodes that have a matching property
942
+ * and value in the data object that was passed into its constructor.
943
+ * @method getNodesByProperty
944
+ * @param {object} property the property to search (usually a string)
945
+ * @param {object} value the value we want to find (usuall an int or string)
946
+ * @return {Array} the matching collection of nodes, null if no match
947
+ */
948
+ getNodesByProperty: function(property, value) {
949
+ var values = [];
950
+ for (var i in this._nodes) {
951
+ if (this._nodes.hasOwnProperty(i)) {
952
+ var n = this._nodes[i];
953
+ if ((property in n && n[property] == value) || (n.data && value == n.data[property])) {
954
+ values.push(n);
955
+ }
956
+ }
957
+ }
958
+
959
+ return (values.length) ? values : null;
960
+ },
961
+
962
+ /**
963
+ * Returns the treeview node reference for an anscestor element
964
+ * of the node, or null if it is not contained within any node
965
+ * in this tree.
966
+ * @method getNodeByElement
967
+ * @param {HTMLElement} the element to test
968
+ * @return {YAHOO.widget.Node} a node reference or null
969
+ */
970
+ getNodeByElement: function(el) {
971
+
972
+ var p=el, m, re=/ygtv([^\d]*)(.*)/;
973
+
974
+ do {
975
+
976
+ if (p && p.id) {
977
+ m = p.id.match(re);
978
+ if (m && m[2]) {
979
+ return this.getNodeByIndex(m[2]);
980
+ }
981
+ }
982
+
983
+ p = p.parentNode;
984
+
985
+ if (!p || !p.tagName) {
986
+ break;
987
+ }
988
+
989
+ }
990
+ while (p.id !== this.id && p.tagName.toLowerCase() !== "body");
991
+
992
+ return null;
993
+ },
994
+
995
+ /**
996
+ * Removes the node and its children, and optionally refreshes the
997
+ * branch of the tree that was affected.
998
+ * @method removeNode
999
+ * @param {Node} The node to remove
1000
+ * @param {boolean} autoRefresh automatically refreshes branch if true
1001
+ * @return {boolean} False is there was a problem, true otherwise.
1002
+ */
1003
+ removeNode: function(node, autoRefresh) {
1004
+
1005
+ // Don't delete the root node
1006
+ if (node.isRoot()) {
1007
+ return false;
1008
+ }
1009
+
1010
+ // Get the branch that we may need to refresh
1011
+ var p = node.parent;
1012
+ if (p.parent) {
1013
+ p = p.parent;
1014
+ }
1015
+
1016
+ // Delete the node and its children
1017
+ this._deleteNode(node);
1018
+
1019
+ // Refresh the parent of the parent
1020
+ if (autoRefresh && p && p.childrenRendered) {
1021
+ p.refresh();
1022
+ }
1023
+
1024
+ return true;
1025
+ },
1026
+
1027
+ /**
1028
+ * wait until the animation is complete before deleting
1029
+ * to avoid javascript errors
1030
+ * @method _removeChildren_animComplete
1031
+ * @param o the custom event payload
1032
+ * @private
1033
+ */
1034
+ _removeChildren_animComplete: function(o) {
1035
+ this.unsubscribe(this._removeChildren_animComplete);
1036
+ this.removeChildren(o.node);
1037
+ },
1038
+
1039
+ /**
1040
+ * Deletes this nodes child collection, recursively. Also collapses
1041
+ * the node, and resets the dynamic load flag. The primary use for
1042
+ * this method is to purge a node and allow it to fetch its data
1043
+ * dynamically again.
1044
+ * @method removeChildren
1045
+ * @param {Node} node the node to purge
1046
+ */
1047
+ removeChildren: function(node) {
1048
+
1049
+ if (node.expanded) {
1050
+ // wait until the animation is complete before deleting to
1051
+ // avoid javascript errors
1052
+ if (this._collapseAnim) {
1053
+ this.subscribe("animComplete",
1054
+ this._removeChildren_animComplete, this, true);
1055
+ Widget.Node.prototype.collapse.call(node);
1056
+ return;
1057
+ }
1058
+
1059
+ node.collapse();
1060
+ }
1061
+
1062
+ while (node.children.length) {
1063
+ this._deleteNode(node.children[0]);
1064
+ }
1065
+
1066
+ if (node.isRoot()) {
1067
+ Widget.Node.prototype.expand.call(node);
1068
+ }
1069
+
1070
+ node.childrenRendered = false;
1071
+ node.dynamicLoadComplete = false;
1072
+
1073
+ node.updateIcon();
1074
+ },
1075
+
1076
+ /**
1077
+ * Deletes the node and recurses children
1078
+ * @method _deleteNode
1079
+ * @private
1080
+ */
1081
+ _deleteNode: function(node) {
1082
+ // Remove all the child nodes first
1083
+ this.removeChildren(node);
1084
+
1085
+ // Remove the node from the tree
1086
+ this.popNode(node);
1087
+ },
1088
+
1089
+ /**
1090
+ * Removes the node from the tree, preserving the child collection
1091
+ * to make it possible to insert the branch into another part of the
1092
+ * tree, or another tree.
1093
+ * @method popNode
1094
+ * @param {Node} the node to remove
1095
+ */
1096
+ popNode: function(node) {
1097
+ var p = node.parent;
1098
+
1099
+ // Update the parent's collection of children
1100
+ var a = [];
1101
+
1102
+ for (var i=0, len=p.children.length;i<len;++i) {
1103
+ if (p.children[i] != node) {
1104
+ a[a.length] = p.children[i];
1105
+ }
1106
+ }
1107
+
1108
+ p.children = a;
1109
+
1110
+ // reset the childrenRendered flag for the parent
1111
+ p.childrenRendered = false;
1112
+
1113
+ // Update the sibling relationship
1114
+ if (node.previousSibling) {
1115
+ node.previousSibling.nextSibling = node.nextSibling;
1116
+ }
1117
+
1118
+ if (node.nextSibling) {
1119
+ node.nextSibling.previousSibling = node.previousSibling;
1120
+ }
1121
+
1122
+ node.parent = null;
1123
+ node.previousSibling = null;
1124
+ node.nextSibling = null;
1125
+ node.tree = null;
1126
+
1127
+ // Update the tree's node collection
1128
+ delete this._nodes[node.index];
1129
+ },
1130
+
1131
+ /**
1132
+ * Nulls out the entire TreeView instance and related objects, removes attached
1133
+ * event listeners, and clears out DOM elements inside the container. After
1134
+ * calling this method, the instance reference should be expliclitly nulled by
1135
+ * implementer, as in myDataTable = null. Use with caution!
1136
+ *
1137
+ * @method destroy
1138
+ */
1139
+ destroy : function() {
1140
+ // Since the label editor can be separated from the main TreeView control
1141
+ // the destroy method for it might not be there.
1142
+ if (this._destroyEditor) { this._destroyEditor(); }
1143
+ var el = this.getEl();
1144
+ Event.removeListener(el,'click');
1145
+ Event.removeListener(el,'dblclick');
1146
+ Event.removeListener(el,'mouseover');
1147
+ Event.removeListener(el,'mouseout');
1148
+ Event.removeListener(el,'keydown');
1149
+ for (var i = 0 ; i < this._nodes.length; i++) {
1150
+ var node = this._nodes[i];
1151
+ if (node && node.destroy) {node.destroy(); }
1152
+ }
1153
+ el.innerHTML = '';
1154
+ this._hasEvents = false;
1155
+ },
1156
+
1157
+
1158
+
1159
+
1160
+ /**
1161
+ * TreeView instance toString
1162
+ * @method toString
1163
+ * @return {string} string representation of the tree
1164
+ */
1165
+ toString: function() {
1166
+ return "TreeView " + this.id;
1167
+ },
1168
+
1169
+ /**
1170
+ * Count of nodes in tree
1171
+ * @method getNodeCount
1172
+ * @return {int} number of nodes in the tree
1173
+ */
1174
+ getNodeCount: function() {
1175
+ return this.getRoot().getNodeCount();
1176
+ },
1177
+
1178
+ /**
1179
+ * Returns an object which could be used to rebuild the tree.
1180
+ * It can be passed to the tree constructor to reproduce the same tree.
1181
+ * It will return false if any node loads dynamically, regardless of whether it is loaded or not.
1182
+ * @method getTreeDefinition
1183
+ * @return {Object | false} definition of the tree or false if any node is defined as dynamic
1184
+ */
1185
+ getTreeDefinition: function() {
1186
+ return this.getRoot().getNodeDefinition();
1187
+ },
1188
+
1189
+ /**
1190
+ * Abstract method that is executed when a node is expanded
1191
+ * @method onExpand
1192
+ * @param node {Node} the node that was expanded
1193
+ * @deprecated use treeobj.subscribe("expand") instead
1194
+ */
1195
+ onExpand: function(node) { },
1196
+
1197
+ /**
1198
+ * Abstract method that is executed when a node is collapsed.
1199
+ * @method onCollapse
1200
+ * @param node {Node} the node that was collapsed.
1201
+ * @deprecated use treeobj.subscribe("collapse") instead
1202
+ */
1203
+ onCollapse: function(node) { },
1204
+
1205
+ /**
1206
+ * Sets the value of a property for all loaded nodes in the tree.
1207
+ * @method setNodesProperty
1208
+ * @param name {string} Name of the property to be set
1209
+ * @param value {any} value to be set
1210
+ * @param refresh {boolean} if present and true, it does a refresh
1211
+ */
1212
+ setNodesProperty: function(name, value, refresh) {
1213
+ this.root.setNodesProperty(name,value);
1214
+ if (refresh) {
1215
+ this.root.refresh();
1216
+ }
1217
+ },
1218
+ /**
1219
+ * Event listener to toggle node highlight.
1220
+ * Can be assigned as listener to clickEvent, dblClickEvent and enterKeyPressed.
1221
+ * It returns false to prevent the default action.
1222
+ * @method onEventToggleHighlight
1223
+ * @param oArgs {any} it takes the arguments of any of the events mentioned above
1224
+ * @return {false} Always cancels the default action for the event
1225
+ */
1226
+ onEventToggleHighlight: function (oArgs) {
1227
+ var node;
1228
+ if ('node' in oArgs && oArgs.node instanceof Widget.Node) {
1229
+ node = oArgs.node;
1230
+ } else if (oArgs instanceof Widget.Node) {
1231
+ node = oArgs;
1232
+ } else {
1233
+ return false;
1234
+ }
1235
+ node.toggleHighlight();
1236
+ return false;
1237
+ }
1238
+
1239
+
1240
+ };
1241
+
1242
+ /* Backwards compatibility aliases */
1243
+ var PROT = TV.prototype;
1244
+ /**
1245
+ * Renders the tree boilerplate and visible nodes.
1246
+ * Alias for render
1247
+ * @method draw
1248
+ * @deprecated Use render instead
1249
+ */
1250
+ PROT.draw = PROT.render;
1251
+
1252
+ /* end backwards compatibility aliases */
1253
+
1254
+ YAHOO.augment(TV, YAHOO.util.EventProvider);
1255
+
1256
+ /**
1257
+ * Running count of all nodes created in all trees. This is
1258
+ * used to provide unique identifies for all nodes. Deleting
1259
+ * nodes does not change the nodeCount.
1260
+ * @property YAHOO.widget.TreeView.nodeCount
1261
+ * @type int
1262
+ * @static
1263
+ */
1264
+ TV.nodeCount = 0;
1265
+
1266
+ /**
1267
+ * Global cache of tree instances
1268
+ * @property YAHOO.widget.TreeView.trees
1269
+ * @type Array
1270
+ * @static
1271
+ * @private
1272
+ */
1273
+ TV.trees = [];
1274
+
1275
+ /**
1276
+ * Global method for getting a tree by its id. Used in the generated
1277
+ * tree html.
1278
+ * @method YAHOO.widget.TreeView.getTree
1279
+ * @param treeId {String} the id of the tree instance
1280
+ * @return {TreeView} the tree instance requested, null if not found.
1281
+ * @static
1282
+ */
1283
+ TV.getTree = function(treeId) {
1284
+ var t = TV.trees[treeId];
1285
+ return (t) ? t : null;
1286
+ };
1287
+
1288
+
1289
+ /**
1290
+ * Global method for getting a node by its id. Used in the generated
1291
+ * tree html.
1292
+ * @method YAHOO.widget.TreeView.getNode
1293
+ * @param treeId {String} the id of the tree instance
1294
+ * @param nodeIndex {String} the index of the node to return
1295
+ * @return {Node} the node instance requested, null if not found
1296
+ * @static
1297
+ */
1298
+ TV.getNode = function(treeId, nodeIndex) {
1299
+ var t = TV.getTree(treeId);
1300
+ return (t) ? t.getNodeByIndex(nodeIndex) : null;
1301
+ };
1302
+
1303
+
1304
+ /**
1305
+ * Class name assigned to elements that have the focus
1306
+ *
1307
+ * @property TreeView.FOCUS_CLASS_NAME
1308
+ * @type String
1309
+ * @static
1310
+ * @final
1311
+ * @default "ygtvfocus"
1312
+
1313
+ */
1314
+ TV.FOCUS_CLASS_NAME = 'ygtvfocus';
1315
+
1316
+ /**
1317
+ * Attempts to preload the images defined in the styles used to draw the tree by
1318
+ * rendering off-screen elements that use the styles.
1319
+ * @method YAHOO.widget.TreeView.preload
1320
+ * @param {string} prefix the prefix to use to generate the names of the
1321
+ * images to preload, default is ygtv
1322
+ * @static
1323
+ */
1324
+ TV.preload = function(e, prefix) {
1325
+ prefix = prefix || "ygtv";
1326
+
1327
+
1328
+ var styles = ["tn","tm","tmh","tp","tph","ln","lm","lmh","lp","lph","loading"];
1329
+ // var styles = ["tp"];
1330
+
1331
+ var sb = [];
1332
+
1333
+ // save the first one for the outer container
1334
+ for (var i=1; i < styles.length; i=i+1) {
1335
+ sb[sb.length] = '<span class="' + prefix + styles[i] + '">&#160;</span>';
1336
+ }
1337
+
1338
+ var f = document.createElement("div");
1339
+ var s = f.style;
1340
+ s.className = prefix + styles[0];
1341
+ s.position = "absolute";
1342
+ s.height = "1px";
1343
+ s.width = "1px";
1344
+ s.top = "-1000px";
1345
+ s.left = "-1000px";
1346
+ f.innerHTML = sb.join("");
1347
+
1348
+ document.body.appendChild(f);
1349
+
1350
+ Event.removeListener(window, "load", TV.preload);
1351
+
1352
+ };
1353
+
1354
+ Event.addListener(window,"load", TV.preload);
1355
+ })();
1356
+ (function () {
1357
+ var Dom = YAHOO.util.Dom,
1358
+ Lang = YAHOO.lang,
1359
+ Event = YAHOO.util.Event;
1360
+ /**
1361
+ * The base class for all tree nodes. The node's presentation and behavior in
1362
+ * response to mouse events is handled in Node subclasses.
1363
+ * @namespace YAHOO.widget
1364
+ * @class Node
1365
+ * @uses YAHOO.util.EventProvider
1366
+ * @param oData {object} a string or object containing the data that will
1367
+ * be used to render this node, and any custom attributes that should be
1368
+ * stored with the node (which is available in noderef.data).
1369
+ * All values in oData will be used to set equally named properties in the node
1370
+ * as long as the node does have such properties, they are not undefined, private or functions,
1371
+ * the rest of the values will be stored in noderef.data
1372
+ * @param oParent {Node} this node's parent node
1373
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated, use oData.expanded)
1374
+ * @constructor
1375
+ */
1376
+ YAHOO.widget.Node = function(oData, oParent, expanded) {
1377
+ if (oData) { this.init(oData, oParent, expanded); }
1378
+ };
1379
+
1380
+ YAHOO.widget.Node.prototype = {
1381
+
1382
+ /**
1383
+ * The index for this instance obtained from global counter in YAHOO.widget.TreeView.
1384
+ * @property index
1385
+ * @type int
1386
+ */
1387
+ index: 0,
1388
+
1389
+ /**
1390
+ * This node's child node collection.
1391
+ * @property children
1392
+ * @type Node[]
1393
+ */
1394
+ children: null,
1395
+
1396
+ /**
1397
+ * Tree instance this node is part of
1398
+ * @property tree
1399
+ * @type TreeView
1400
+ */
1401
+ tree: null,
1402
+
1403
+ /**
1404
+ * The data linked to this node. This can be any object or primitive
1405
+ * value, and the data can be used in getNodeHtml().
1406
+ * @property data
1407
+ * @type object
1408
+ */
1409
+ data: null,
1410
+
1411
+ /**
1412
+ * Parent node
1413
+ * @property parent
1414
+ * @type Node
1415
+ */
1416
+ parent: null,
1417
+
1418
+ /**
1419
+ * The depth of this node. We start at -1 for the root node.
1420
+ * @property depth
1421
+ * @type int
1422
+ */
1423
+ depth: -1,
1424
+
1425
+ /**
1426
+ * The node's expanded/collapsed state
1427
+ * @property expanded
1428
+ * @type boolean
1429
+ */
1430
+ expanded: false,
1431
+
1432
+ /**
1433
+ * Can multiple children be expanded at once?
1434
+ * @property multiExpand
1435
+ * @type boolean
1436
+ */
1437
+ multiExpand: true,
1438
+
1439
+ /**
1440
+ * Should we render children for a collapsed node? It is possible that the
1441
+ * implementer will want to render the hidden data... @todo verify that we
1442
+ * need this, and implement it if we do.
1443
+ * @property renderHidden
1444
+ * @type boolean
1445
+ */
1446
+ renderHidden: false,
1447
+
1448
+ /**
1449
+ * This flag is set to true when the html is generated for this node's
1450
+ * children, and set to false when new children are added.
1451
+ * @property childrenRendered
1452
+ * @type boolean
1453
+ */
1454
+ childrenRendered: false,
1455
+
1456
+ /**
1457
+ * Dynamically loaded nodes only fetch the data the first time they are
1458
+ * expanded. This flag is set to true once the data has been fetched.
1459
+ * @property dynamicLoadComplete
1460
+ * @type boolean
1461
+ */
1462
+ dynamicLoadComplete: false,
1463
+
1464
+ /**
1465
+ * This node's previous sibling
1466
+ * @property previousSibling
1467
+ * @type Node
1468
+ */
1469
+ previousSibling: null,
1470
+
1471
+ /**
1472
+ * This node's next sibling
1473
+ * @property nextSibling
1474
+ * @type Node
1475
+ */
1476
+ nextSibling: null,
1477
+
1478
+ /**
1479
+ * We can set the node up to call an external method to get the child
1480
+ * data dynamically.
1481
+ * @property _dynLoad
1482
+ * @type boolean
1483
+ * @private
1484
+ */
1485
+ _dynLoad: false,
1486
+
1487
+ /**
1488
+ * Function to execute when we need to get this node's child data.
1489
+ * @property dataLoader
1490
+ * @type function
1491
+ */
1492
+ dataLoader: null,
1493
+
1494
+ /**
1495
+ * This is true for dynamically loading nodes while waiting for the
1496
+ * callback to return.
1497
+ * @property isLoading
1498
+ * @type boolean
1499
+ */
1500
+ isLoading: false,
1501
+
1502
+ /**
1503
+ * The toggle/branch icon will not show if this is set to false. This
1504
+ * could be useful if the implementer wants to have the child contain
1505
+ * extra info about the parent, rather than an actual node.
1506
+ * @property hasIcon
1507
+ * @type boolean
1508
+ */
1509
+ hasIcon: true,
1510
+
1511
+ /**
1512
+ * Used to configure what happens when a dynamic load node is expanded
1513
+ * and we discover that it does not have children. By default, it is
1514
+ * treated as if it still could have children (plus/minus icon). Set
1515
+ * iconMode to have it display like a leaf node instead.
1516
+ * @property iconMode
1517
+ * @type int
1518
+ */
1519
+ iconMode: 0,
1520
+
1521
+ /**
1522
+ * Specifies whether or not the content area of the node should be allowed
1523
+ * to wrap.
1524
+ * @property nowrap
1525
+ * @type boolean
1526
+ * @default false
1527
+ */
1528
+ nowrap: false,
1529
+
1530
+ /**
1531
+ * If true, the node will alway be rendered as a leaf node. This can be
1532
+ * used to override the presentation when dynamically loading the entire
1533
+ * tree. Setting this to true also disables the dynamic load call for the
1534
+ * node.
1535
+ * @property isLeaf
1536
+ * @type boolean
1537
+ * @default false
1538
+ */
1539
+ isLeaf: false,
1540
+
1541
+ /**
1542
+ * The CSS class for the html content container. Defaults to ygtvhtml, but
1543
+ * can be overridden to provide a custom presentation for a specific node.
1544
+ * @property contentStyle
1545
+ * @type string
1546
+ */
1547
+ contentStyle: "",
1548
+
1549
+
1550
+ /**
1551
+ * The generated id that will contain the data passed in by the implementer.
1552
+ * @property contentElId
1553
+ * @type string
1554
+ */
1555
+ contentElId: null,
1556
+
1557
+ /**
1558
+ * Enables node highlighting. If true, the node can be highlighted and/or propagate highlighting
1559
+ * @property enableHighlight
1560
+ * @type boolean
1561
+ * @default true
1562
+ */
1563
+ enableHighlight: true,
1564
+
1565
+ /**
1566
+ * Stores the highlight state. Can be any of:
1567
+ * <ul>
1568
+ * <li>0 - not highlighted</li>
1569
+ * <li>1 - highlighted</li>
1570
+ * <li>2 - some children highlighted</li>
1571
+ * </ul>
1572
+ * @property highlightState
1573
+ * @type integer
1574
+ * @default 0
1575
+ */
1576
+
1577
+ highlightState: 0,
1578
+
1579
+ /**
1580
+ * Tells whether highlighting will be propagated up to the parents of the clicked node
1581
+ * @property propagateHighlightUp
1582
+ * @type boolean
1583
+ * @default false
1584
+ */
1585
+
1586
+ propagateHighlightUp: false,
1587
+
1588
+ /**
1589
+ * Tells whether highlighting will be propagated down to the children of the clicked node
1590
+ * @property propagateHighlightDown
1591
+ * @type boolean
1592
+ * @default false
1593
+ */
1594
+
1595
+ propagateHighlightDown: false,
1596
+
1597
+ /**
1598
+ * User-defined className to be added to the Node
1599
+ * @property className
1600
+ * @type string
1601
+ * @default null
1602
+ */
1603
+
1604
+ className: null,
1605
+
1606
+ /**
1607
+ * The node type
1608
+ * @property _type
1609
+ * @private
1610
+ * @type string
1611
+ * @default "Node"
1612
+ */
1613
+ _type: "Node",
1614
+
1615
+ /*
1616
+ spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif",
1617
+ expandedText: "Expanded",
1618
+ collapsedText: "Collapsed",
1619
+ loadingText: "Loading",
1620
+ */
1621
+
1622
+ /**
1623
+ * Initializes this node, gets some of the properties from the parent
1624
+ * @method init
1625
+ * @param oData {object} a string or object containing the data that will
1626
+ * be used to render this node
1627
+ * @param oParent {Node} this node's parent node
1628
+ * @param expanded {boolean} the initial expanded/collapsed state
1629
+ */
1630
+ init: function(oData, oParent, expanded) {
1631
+
1632
+ this.data = {};
1633
+ this.children = [];
1634
+ this.index = YAHOO.widget.TreeView.nodeCount;
1635
+ ++YAHOO.widget.TreeView.nodeCount;
1636
+ this.contentElId = "ygtvcontentel" + this.index;
1637
+
1638
+ if (Lang.isObject(oData)) {
1639
+ for (var property in oData) {
1640
+ if (oData.hasOwnProperty(property)) {
1641
+ if (property.charAt(0) != '_' && !Lang.isUndefined(this[property]) && !Lang.isFunction(this[property]) ) {
1642
+ this[property] = oData[property];
1643
+ } else {
1644
+ this.data[property] = oData[property];
1645
+ }
1646
+ }
1647
+ }
1648
+ }
1649
+ if (!Lang.isUndefined(expanded) ) { this.expanded = expanded; }
1650
+
1651
+
1652
+ /**
1653
+ * The parentChange event is fired when a parent element is applied
1654
+ * to the node. This is useful if you need to apply tree-level
1655
+ * properties to a tree that need to happen if a node is moved from
1656
+ * one tree to another.
1657
+ *
1658
+ * @event parentChange
1659
+ * @type CustomEvent
1660
+ */
1661
+ this.createEvent("parentChange", this);
1662
+
1663
+ // oParent should never be null except when we create the root node.
1664
+ if (oParent) {
1665
+ oParent.appendChild(this);
1666
+ }
1667
+ },
1668
+
1669
+ /**
1670
+ * Certain properties for the node cannot be set until the parent
1671
+ * is known. This is called after the node is inserted into a tree.
1672
+ * the parent is also applied to this node's children in order to
1673
+ * make it possible to move a branch from one tree to another.
1674
+ * @method applyParent
1675
+ * @param {Node} parentNode this node's parent node
1676
+ * @return {boolean} true if the application was successful
1677
+ */
1678
+ applyParent: function(parentNode) {
1679
+ if (!parentNode) {
1680
+ return false;
1681
+ }
1682
+
1683
+ this.tree = parentNode.tree;
1684
+ this.parent = parentNode;
1685
+ this.depth = parentNode.depth + 1;
1686
+
1687
+ // @todo why was this put here. This causes new nodes added at the
1688
+ // root level to lose the menu behavior.
1689
+ // if (! this.multiExpand) {
1690
+ // this.multiExpand = parentNode.multiExpand;
1691
+ // }
1692
+
1693
+ this.tree.regNode(this);
1694
+ parentNode.childrenRendered = false;
1695
+
1696
+ // cascade update existing children
1697
+ for (var i=0, len=this.children.length;i<len;++i) {
1698
+ this.children[i].applyParent(this);
1699
+ }
1700
+
1701
+ this.fireEvent("parentChange");
1702
+
1703
+ return true;
1704
+ },
1705
+
1706
+ /**
1707
+ * Appends a node to the child collection.
1708
+ * @method appendChild
1709
+ * @param childNode {Node} the new node
1710
+ * @return {Node} the child node
1711
+ * @private
1712
+ */
1713
+ appendChild: function(childNode) {
1714
+ if (this.hasChildren()) {
1715
+ var sib = this.children[this.children.length - 1];
1716
+ sib.nextSibling = childNode;
1717
+ childNode.previousSibling = sib;
1718
+ }
1719
+ this.children[this.children.length] = childNode;
1720
+ childNode.applyParent(this);
1721
+
1722
+ // part of the IE display issue workaround. If child nodes
1723
+ // are added after the initial render, and the node was
1724
+ // instantiated with expanded = true, we need to show the
1725
+ // children div now that the node has a child.
1726
+ if (this.childrenRendered && this.expanded) {
1727
+ this.getChildrenEl().style.display = "";
1728
+ }
1729
+
1730
+ return childNode;
1731
+ },
1732
+
1733
+ /**
1734
+ * Appends this node to the supplied node's child collection
1735
+ * @method appendTo
1736
+ * @param parentNode {Node} the node to append to.
1737
+ * @return {Node} The appended node
1738
+ */
1739
+ appendTo: function(parentNode) {
1740
+ return parentNode.appendChild(this);
1741
+ },
1742
+
1743
+ /**
1744
+ * Inserts this node before this supplied node
1745
+ * @method insertBefore
1746
+ * @param node {Node} the node to insert this node before
1747
+ * @return {Node} the inserted node
1748
+ */
1749
+ insertBefore: function(node) {
1750
+ var p = node.parent;
1751
+ if (p) {
1752
+
1753
+ if (this.tree) {
1754
+ this.tree.popNode(this);
1755
+ }
1756
+
1757
+ var refIndex = node.isChildOf(p);
1758
+ p.children.splice(refIndex, 0, this);
1759
+ if (node.previousSibling) {
1760
+ node.previousSibling.nextSibling = this;
1761
+ }
1762
+ this.previousSibling = node.previousSibling;
1763
+ this.nextSibling = node;
1764
+ node.previousSibling = this;
1765
+
1766
+ this.applyParent(p);
1767
+ }
1768
+
1769
+ return this;
1770
+ },
1771
+
1772
+ /**
1773
+ * Inserts this node after the supplied node
1774
+ * @method insertAfter
1775
+ * @param node {Node} the node to insert after
1776
+ * @return {Node} the inserted node
1777
+ */
1778
+ insertAfter: function(node) {
1779
+ var p = node.parent;
1780
+ if (p) {
1781
+
1782
+ if (this.tree) {
1783
+ this.tree.popNode(this);
1784
+ }
1785
+
1786
+ var refIndex = node.isChildOf(p);
1787
+
1788
+ if (!node.nextSibling) {
1789
+ this.nextSibling = null;
1790
+ return this.appendTo(p);
1791
+ }
1792
+
1793
+ p.children.splice(refIndex + 1, 0, this);
1794
+
1795
+ node.nextSibling.previousSibling = this;
1796
+ this.previousSibling = node;
1797
+ this.nextSibling = node.nextSibling;
1798
+ node.nextSibling = this;
1799
+
1800
+ this.applyParent(p);
1801
+ }
1802
+
1803
+ return this;
1804
+ },
1805
+
1806
+ /**
1807
+ * Returns true if the Node is a child of supplied Node
1808
+ * @method isChildOf
1809
+ * @param parentNode {Node} the Node to check
1810
+ * @return {boolean} The node index if this Node is a child of
1811
+ * supplied Node, else -1.
1812
+ * @private
1813
+ */
1814
+ isChildOf: function(parentNode) {
1815
+ if (parentNode && parentNode.children) {
1816
+ for (var i=0, len=parentNode.children.length; i<len ; ++i) {
1817
+ if (parentNode.children[i] === this) {
1818
+ return i;
1819
+ }
1820
+ }
1821
+ }
1822
+
1823
+ return -1;
1824
+ },
1825
+
1826
+ /**
1827
+ * Returns a node array of this node's siblings, null if none.
1828
+ * @method getSiblings
1829
+ * @return Node[]
1830
+ */
1831
+ getSiblings: function() {
1832
+ var sib = this.parent.children.slice(0);
1833
+ for (var i=0;i < sib.length && sib[i] != this;i++) {}
1834
+ sib.splice(i,1);
1835
+ if (sib.length) { return sib; }
1836
+ return null;
1837
+ },
1838
+
1839
+ /**
1840
+ * Shows this node's children
1841
+ * @method showChildren
1842
+ */
1843
+ showChildren: function() {
1844
+ if (!this.tree.animateExpand(this.getChildrenEl(), this)) {
1845
+ if (this.hasChildren()) {
1846
+ this.getChildrenEl().style.display = "";
1847
+ }
1848
+ }
1849
+ },
1850
+
1851
+ /**
1852
+ * Hides this node's children
1853
+ * @method hideChildren
1854
+ */
1855
+ hideChildren: function() {
1856
+
1857
+ if (!this.tree.animateCollapse(this.getChildrenEl(), this)) {
1858
+ this.getChildrenEl().style.display = "none";
1859
+ }
1860
+ },
1861
+
1862
+ /**
1863
+ * Returns the id for this node's container div
1864
+ * @method getElId
1865
+ * @return {string} the element id
1866
+ */
1867
+ getElId: function() {
1868
+ return "ygtv" + this.index;
1869
+ },
1870
+
1871
+ /**
1872
+ * Returns the id for this node's children div
1873
+ * @method getChildrenElId
1874
+ * @return {string} the element id for this node's children div
1875
+ */
1876
+ getChildrenElId: function() {
1877
+ return "ygtvc" + this.index;
1878
+ },
1879
+
1880
+ /**
1881
+ * Returns the id for this node's toggle element
1882
+ * @method getToggleElId
1883
+ * @return {string} the toggel element id
1884
+ */
1885
+ getToggleElId: function() {
1886
+ return "ygtvt" + this.index;
1887
+ },
1888
+
1889
+
1890
+ /*
1891
+ * Returns the id for this node's spacer image. The spacer is positioned
1892
+ * over the toggle and provides feedback for screen readers.
1893
+ * @method getSpacerId
1894
+ * @return {string} the id for the spacer image
1895
+ */
1896
+ /*
1897
+ getSpacerId: function() {
1898
+ return "ygtvspacer" + this.index;
1899
+ },
1900
+ */
1901
+
1902
+ /**
1903
+ * Returns this node's container html element
1904
+ * @method getEl
1905
+ * @return {HTMLElement} the container html element
1906
+ */
1907
+ getEl: function() {
1908
+ return Dom.get(this.getElId());
1909
+ },
1910
+
1911
+ /**
1912
+ * Returns the div that was generated for this node's children
1913
+ * @method getChildrenEl
1914
+ * @return {HTMLElement} this node's children div
1915
+ */
1916
+ getChildrenEl: function() {
1917
+ return Dom.get(this.getChildrenElId());
1918
+ },
1919
+
1920
+ /**
1921
+ * Returns the element that is being used for this node's toggle.
1922
+ * @method getToggleEl
1923
+ * @return {HTMLElement} this node's toggle html element
1924
+ */
1925
+ getToggleEl: function() {
1926
+ return Dom.get(this.getToggleElId());
1927
+ },
1928
+ /**
1929
+ * Returns the outer html element for this node's content
1930
+ * @method getContentEl
1931
+ * @return {HTMLElement} the element
1932
+ */
1933
+ getContentEl: function() {
1934
+ return Dom.get(this.contentElId);
1935
+ },
1936
+
1937
+
1938
+ /*
1939
+ * Returns the element that is being used for this node's spacer.
1940
+ * @method getSpacer
1941
+ * @return {HTMLElement} this node's spacer html element
1942
+ */
1943
+ /*
1944
+ getSpacer: function() {
1945
+ return document.getElementById( this.getSpacerId() ) || {};
1946
+ },
1947
+ */
1948
+
1949
+ /*
1950
+ getStateText: function() {
1951
+ if (this.isLoading) {
1952
+ return this.loadingText;
1953
+ } else if (this.hasChildren(true)) {
1954
+ if (this.expanded) {
1955
+ return this.expandedText;
1956
+ } else {
1957
+ return this.collapsedText;
1958
+ }
1959
+ } else {
1960
+ return "";
1961
+ }
1962
+ },
1963
+ */
1964
+
1965
+ /**
1966
+ * Hides this nodes children (creating them if necessary), changes the toggle style.
1967
+ * @method collapse
1968
+ */
1969
+ collapse: function() {
1970
+ // Only collapse if currently expanded
1971
+ if (!this.expanded) { return; }
1972
+
1973
+ // fire the collapse event handler
1974
+ var ret = this.tree.onCollapse(this);
1975
+
1976
+ if (false === ret) {
1977
+ return;
1978
+ }
1979
+
1980
+ ret = this.tree.fireEvent("collapse", this);
1981
+
1982
+ if (false === ret) {
1983
+ return;
1984
+ }
1985
+
1986
+
1987
+ if (!this.getEl()) {
1988
+ this.expanded = false;
1989
+ } else {
1990
+ // hide the child div
1991
+ this.hideChildren();
1992
+ this.expanded = false;
1993
+
1994
+ this.updateIcon();
1995
+ }
1996
+
1997
+ // this.getSpacer().title = this.getStateText();
1998
+
1999
+ ret = this.tree.fireEvent("collapseComplete", this);
2000
+
2001
+ },
2002
+
2003
+ /**
2004
+ * Shows this nodes children (creating them if necessary), changes the
2005
+ * toggle style, and collapses its siblings if multiExpand is not set.
2006
+ * @method expand
2007
+ */
2008
+ expand: function(lazySource) {
2009
+ // Only expand if currently collapsed.
2010
+ if (this.expanded && !lazySource) {
2011
+ return;
2012
+ }
2013
+
2014
+ var ret = true;
2015
+
2016
+ // When returning from the lazy load handler, expand is called again
2017
+ // in order to render the new children. The "expand" event already
2018
+ // fired before fething the new data, so we need to skip it now.
2019
+ if (!lazySource) {
2020
+ // fire the expand event handler
2021
+ ret = this.tree.onExpand(this);
2022
+
2023
+ if (false === ret) {
2024
+ return;
2025
+ }
2026
+
2027
+ ret = this.tree.fireEvent("expand", this);
2028
+ }
2029
+
2030
+ if (false === ret) {
2031
+ return;
2032
+ }
2033
+
2034
+ if (!this.getEl()) {
2035
+ this.expanded = true;
2036
+ return;
2037
+ }
2038
+
2039
+ if (!this.childrenRendered) {
2040
+ this.getChildrenEl().innerHTML = this.renderChildren();
2041
+ } else {
2042
+ }
2043
+
2044
+ this.expanded = true;
2045
+
2046
+ this.updateIcon();
2047
+
2048
+ // this.getSpacer().title = this.getStateText();
2049
+
2050
+ // We do an extra check for children here because the lazy
2051
+ // load feature can expose nodes that have no children.
2052
+
2053
+ // if (!this.hasChildren()) {
2054
+ if (this.isLoading) {
2055
+ this.expanded = false;
2056
+ return;
2057
+ }
2058
+
2059
+ if (! this.multiExpand) {
2060
+ var sibs = this.getSiblings();
2061
+ for (var i=0; sibs && i<sibs.length; ++i) {
2062
+ if (sibs[i] != this && sibs[i].expanded) {
2063
+ sibs[i].collapse();
2064
+ }
2065
+ }
2066
+ }
2067
+
2068
+ this.showChildren();
2069
+
2070
+ ret = this.tree.fireEvent("expandComplete", this);
2071
+ },
2072
+
2073
+ updateIcon: function() {
2074
+ if (this.hasIcon) {
2075
+ var el = this.getToggleEl();
2076
+ if (el) {
2077
+ el.className = el.className.replace(/\bygtv(([tl][pmn]h?)|(loading))\b/gi,this.getStyle());
2078
+ }
2079
+ }
2080
+ },
2081
+
2082
+ /**
2083
+ * Returns the css style name for the toggle
2084
+ * @method getStyle
2085
+ * @return {string} the css class for this node's toggle
2086
+ */
2087
+ getStyle: function() {
2088
+ if (this.isLoading) {
2089
+ return "ygtvloading";
2090
+ } else {
2091
+ // location top or bottom, middle nodes also get the top style
2092
+ var loc = (this.nextSibling) ? "t" : "l";
2093
+
2094
+ // type p=plus(expand), m=minus(collapase), n=none(no children)
2095
+ var type = "n";
2096
+ if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) {
2097
+ // if (this.hasChildren(true)) {
2098
+ type = (this.expanded) ? "m" : "p";
2099
+ }
2100
+
2101
+ return "ygtv" + loc + type;
2102
+ }
2103
+ },
2104
+
2105
+ /**
2106
+ * Returns the hover style for the icon
2107
+ * @return {string} the css class hover state
2108
+ * @method getHoverStyle
2109
+ */
2110
+ getHoverStyle: function() {
2111
+ var s = this.getStyle();
2112
+ if (this.hasChildren(true) && !this.isLoading) {
2113
+ s += "h";
2114
+ }
2115
+ return s;
2116
+ },
2117
+
2118
+ /**
2119
+ * Recursively expands all of this node's children.
2120
+ * @method expandAll
2121
+ */
2122
+ expandAll: function() {
2123
+ var l = this.children.length;
2124
+ for (var i=0;i<l;++i) {
2125
+ var c = this.children[i];
2126
+ if (c.isDynamic()) {
2127
+ break;
2128
+ } else if (! c.multiExpand) {
2129
+ break;
2130
+ } else {
2131
+ c.expand();
2132
+ c.expandAll();
2133
+ }
2134
+ }
2135
+ },
2136
+
2137
+ /**
2138
+ * Recursively collapses all of this node's children.
2139
+ * @method collapseAll
2140
+ */
2141
+ collapseAll: function() {
2142
+ for (var i=0;i<this.children.length;++i) {
2143
+ this.children[i].collapse();
2144
+ this.children[i].collapseAll();
2145
+ }
2146
+ },
2147
+
2148
+ /**
2149
+ * Configures this node for dynamically obtaining the child data
2150
+ * when the node is first expanded. Calling it without the callback
2151
+ * will turn off dynamic load for the node.
2152
+ * @method setDynamicLoad
2153
+ * @param fmDataLoader {function} the function that will be used to get the data.
2154
+ * @param iconMode {int} configures the icon that is displayed when a dynamic
2155
+ * load node is expanded the first time without children. By default, the
2156
+ * "collapse" icon will be used. If set to 1, the leaf node icon will be
2157
+ * displayed.
2158
+ */
2159
+ setDynamicLoad: function(fnDataLoader, iconMode) {
2160
+ if (fnDataLoader) {
2161
+ this.dataLoader = fnDataLoader;
2162
+ this._dynLoad = true;
2163
+ } else {
2164
+ this.dataLoader = null;
2165
+ this._dynLoad = false;
2166
+ }
2167
+
2168
+ if (iconMode) {
2169
+ this.iconMode = iconMode;
2170
+ }
2171
+ },
2172
+
2173
+ /**
2174
+ * Evaluates if this node is the root node of the tree
2175
+ * @method isRoot
2176
+ * @return {boolean} true if this is the root node
2177
+ */
2178
+ isRoot: function() {
2179
+ return (this == this.tree.root);
2180
+ },
2181
+
2182
+ /**
2183
+ * Evaluates if this node's children should be loaded dynamically. Looks for
2184
+ * the property both in this instance and the root node. If the tree is
2185
+ * defined to load all children dynamically, the data callback function is
2186
+ * defined in the root node
2187
+ * @method isDynamic
2188
+ * @return {boolean} true if this node's children are to be loaded dynamically
2189
+ */
2190
+ isDynamic: function() {
2191
+ if (this.isLeaf) {
2192
+ return false;
2193
+ } else {
2194
+ return (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad));
2195
+ // return lazy;
2196
+ }
2197
+ },
2198
+
2199
+ /**
2200
+ * Returns the current icon mode. This refers to the way childless dynamic
2201
+ * load nodes appear (this comes into play only after the initial dynamic
2202
+ * load request produced no children).
2203
+ * @method getIconMode
2204
+ * @return {int} 0 for collapse style, 1 for leaf node style
2205
+ */
2206
+ getIconMode: function() {
2207
+ return (this.iconMode || this.tree.root.iconMode);
2208
+ },
2209
+
2210
+ /**
2211
+ * Checks if this node has children. If this node is lazy-loading and the
2212
+ * children have not been rendered, we do not know whether or not there
2213
+ * are actual children. In most cases, we need to assume that there are
2214
+ * children (for instance, the toggle needs to show the expandable
2215
+ * presentation state). In other times we want to know if there are rendered
2216
+ * children. For the latter, "checkForLazyLoad" should be false.
2217
+ * @method hasChildren
2218
+ * @param checkForLazyLoad {boolean} should we check for unloaded children?
2219
+ * @return {boolean} true if this has children or if it might and we are
2220
+ * checking for this condition.
2221
+ */
2222
+ hasChildren: function(checkForLazyLoad) {
2223
+ if (this.isLeaf) {
2224
+ return false;
2225
+ } else {
2226
+ return ( this.children.length > 0 ||
2227
+ (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) );
2228
+ }
2229
+ },
2230
+
2231
+ /**
2232
+ * Expands if node is collapsed, collapses otherwise.
2233
+ * @method toggle
2234
+ */
2235
+ toggle: function() {
2236
+ if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) {
2237
+ if (this.expanded) { this.collapse(); } else { this.expand(); }
2238
+ }
2239
+ },
2240
+
2241
+ /**
2242
+ * Returns the markup for this node and its children.
2243
+ * @method getHtml
2244
+ * @return {string} the markup for this node and its expanded children.
2245
+ */
2246
+ getHtml: function() {
2247
+
2248
+ this.childrenRendered = false;
2249
+
2250
+ return ['<div class="ygtvitem" id="' , this.getElId() , '">' ,this.getNodeHtml() , this.getChildrenHtml() ,'</div>'].join("");
2251
+ },
2252
+
2253
+ /**
2254
+ * Called when first rendering the tree. We always build the div that will
2255
+ * contain this nodes children, but we don't render the children themselves
2256
+ * unless this node is expanded.
2257
+ * @method getChildrenHtml
2258
+ * @return {string} the children container div html and any expanded children
2259
+ * @private
2260
+ */
2261
+ getChildrenHtml: function() {
2262
+
2263
+
2264
+ var sb = [];
2265
+ sb[sb.length] = '<div class="ygtvchildren" id="' + this.getChildrenElId() + '"';
2266
+
2267
+ // This is a workaround for an IE rendering issue, the child div has layout
2268
+ // in IE, creating extra space if a leaf node is created with the expanded
2269
+ // property set to true.
2270
+ if (!this.expanded || !this.hasChildren()) {
2271
+ sb[sb.length] = ' style="display:none;"';
2272
+ }
2273
+ sb[sb.length] = '>';
2274
+
2275
+
2276
+ // Don't render the actual child node HTML unless this node is expanded.
2277
+ if ( (this.hasChildren(true) && this.expanded) ||
2278
+ (this.renderHidden && !this.isDynamic()) ) {
2279
+ sb[sb.length] = this.renderChildren();
2280
+ }
2281
+
2282
+ sb[sb.length] = '</div>';
2283
+
2284
+ return sb.join("");
2285
+ },
2286
+
2287
+ /**
2288
+ * Generates the markup for the child nodes. This is not done until the node
2289
+ * is expanded.
2290
+ * @method renderChildren
2291
+ * @return {string} the html for this node's children
2292
+ * @private
2293
+ */
2294
+ renderChildren: function() {
2295
+
2296
+
2297
+ var node = this;
2298
+
2299
+ if (this.isDynamic() && !this.dynamicLoadComplete) {
2300
+ this.isLoading = true;
2301
+ this.tree.locked = true;
2302
+
2303
+ if (this.dataLoader) {
2304
+
2305
+ setTimeout(
2306
+ function() {
2307
+ node.dataLoader(node,
2308
+ function() {
2309
+ node.loadComplete();
2310
+ });
2311
+ }, 10);
2312
+
2313
+ } else if (this.tree.root.dataLoader) {
2314
+
2315
+ setTimeout(
2316
+ function() {
2317
+ node.tree.root.dataLoader(node,
2318
+ function() {
2319
+ node.loadComplete();
2320
+ });
2321
+ }, 10);
2322
+
2323
+ } else {
2324
+ return "Error: data loader not found or not specified.";
2325
+ }
2326
+
2327
+ return "";
2328
+
2329
+ } else {
2330
+ return this.completeRender();
2331
+ }
2332
+ },
2333
+
2334
+ /**
2335
+ * Called when we know we have all the child data.
2336
+ * @method completeRender
2337
+ * @return {string} children html
2338
+ */
2339
+ completeRender: function() {
2340
+ var sb = [];
2341
+
2342
+ for (var i=0; i < this.children.length; ++i) {
2343
+ // this.children[i].childrenRendered = false;
2344
+ sb[sb.length] = this.children[i].getHtml();
2345
+ }
2346
+
2347
+ this.childrenRendered = true;
2348
+
2349
+ return sb.join("");
2350
+ },
2351
+
2352
+ /**
2353
+ * Load complete is the callback function we pass to the data provider
2354
+ * in dynamic load situations.
2355
+ * @method loadComplete
2356
+ */
2357
+ loadComplete: function() {
2358
+ this.getChildrenEl().innerHTML = this.completeRender();
2359
+ this.dynamicLoadComplete = true;
2360
+ this.isLoading = false;
2361
+ this.expand(true);
2362
+ this.tree.locked = false;
2363
+ },
2364
+
2365
+ /**
2366
+ * Returns this node's ancestor at the specified depth.
2367
+ * @method getAncestor
2368
+ * @param {int} depth the depth of the ancestor.
2369
+ * @return {Node} the ancestor
2370
+ */
2371
+ getAncestor: function(depth) {
2372
+ if (depth >= this.depth || depth < 0) {
2373
+ return null;
2374
+ }
2375
+
2376
+ var p = this.parent;
2377
+
2378
+ while (p.depth > depth) {
2379
+ p = p.parent;
2380
+ }
2381
+
2382
+ return p;
2383
+ },
2384
+
2385
+ /**
2386
+ * Returns the css class for the spacer at the specified depth for
2387
+ * this node. If this node's ancestor at the specified depth
2388
+ * has a next sibling the presentation is different than if it
2389
+ * does not have a next sibling
2390
+ * @method getDepthStyle
2391
+ * @param {int} depth the depth of the ancestor.
2392
+ * @return {string} the css class for the spacer
2393
+ */
2394
+ getDepthStyle: function(depth) {
2395
+ return (this.getAncestor(depth).nextSibling) ?
2396
+ "ygtvdepthcell" : "ygtvblankdepthcell";
2397
+ },
2398
+
2399
+ /**
2400
+ * Get the markup for the node. This may be overrided so that we can
2401
+ * support different types of nodes.
2402
+ * @method getNodeHtml
2403
+ * @return {string} The HTML that will render this node.
2404
+ */
2405
+ getNodeHtml: function() {
2406
+ var sb = [];
2407
+
2408
+ sb[sb.length] = '<table id="ygtvtableel' + this.index + '"border="0" cellpadding="0" cellspacing="0" class="ygtvtable ygtvdepth' + this.depth;
2409
+ if (this.enableHighlight) {
2410
+ sb[sb.length] = ' ygtv-highlight' + this.highlightState;
2411
+ }
2412
+ if (this.className) {
2413
+ sb[sb.length] = ' ' + this.className;
2414
+ }
2415
+ sb[sb.length] = '"><tr class="ygtvrow">';
2416
+
2417
+ for (var i=0;i<this.depth;++i) {
2418
+ sb[sb.length] = '<td class="ygtvcell ' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>';
2419
+ }
2420
+
2421
+ if (this.hasIcon) {
2422
+ sb[sb.length] = '<td id="' + this.getToggleElId();
2423
+ sb[sb.length] = '" class="ygtvcell ';
2424
+ sb[sb.length] = this.getStyle() ;
2425
+ sb[sb.length] = '"><div class="ygtvspacer">&nbsp;</div></td>';
2426
+ }
2427
+
2428
+ sb[sb.length] = '<td id="' + this.contentElId;
2429
+ sb[sb.length] = '" class="ygtvcell ';
2430
+ sb[sb.length] = this.contentStyle + ' ygtvcontent" ';
2431
+ sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : '';
2432
+ sb[sb.length] = ' >';
2433
+ sb[sb.length] = this.getContentHtml();
2434
+ sb[sb.length] = '</td></tr></table>';
2435
+
2436
+ return sb.join("");
2437
+
2438
+ },
2439
+ /**
2440
+ * Get the markup for the contents of the node. This is designed to be overrided so that we can
2441
+ * support different types of nodes.
2442
+ * @method getContentHtml
2443
+ * @return {string} The HTML that will render the content of this node.
2444
+ */
2445
+ getContentHtml: function () {
2446
+ return "";
2447
+ },
2448
+
2449
+ /**
2450
+ * Regenerates the html for this node and its children. To be used when the
2451
+ * node is expanded and new children have been added.
2452
+ * @method refresh
2453
+ */
2454
+ refresh: function() {
2455
+ // this.loadComplete();
2456
+ this.getChildrenEl().innerHTML = this.completeRender();
2457
+
2458
+ if (this.hasIcon) {
2459
+ var el = this.getToggleEl();
2460
+ if (el) {
2461
+ el.className = el.className.replace(/\bygtv[lt][nmp]h*\b/gi,this.getStyle());
2462
+ }
2463
+ }
2464
+ },
2465
+
2466
+ /**
2467
+ * Node toString
2468
+ * @method toString
2469
+ * @return {string} string representation of the node
2470
+ */
2471
+ toString: function() {
2472
+ return this._type + " (" + this.index + ")";
2473
+ },
2474
+ /**
2475
+ * array of items that had the focus set on them
2476
+ * so that they can be cleaned when focus is lost
2477
+ * @property _focusHighlightedItems
2478
+ * @type Array of DOM elements
2479
+ * @private
2480
+ */
2481
+ _focusHighlightedItems: [],
2482
+ /**
2483
+ * DOM element that actually got the browser focus
2484
+ * @property _focusedItem
2485
+ * @type DOM element
2486
+ * @private
2487
+ */
2488
+ _focusedItem: null,
2489
+
2490
+ /**
2491
+ * Returns true if there are any elements in the node that can
2492
+ * accept the real actual browser focus
2493
+ * @method _canHaveFocus
2494
+ * @return {boolean} success
2495
+ * @private
2496
+ */
2497
+ _canHaveFocus: function() {
2498
+ return this.getEl().getElementsByTagName('a').length > 0;
2499
+ },
2500
+ /**
2501
+ * Removes the focus of previously selected Node
2502
+ * @method _removeFocus
2503
+ * @private
2504
+ */
2505
+ _removeFocus:function () {
2506
+ if (this._focusedItem) {
2507
+ Event.removeListener(this._focusedItem,'blur');
2508
+ this._focusedItem = null;
2509
+ }
2510
+ var el;
2511
+ while ((el = this._focusHighlightedItems.shift())) { // yes, it is meant as an assignment, really
2512
+ Dom.removeClass(el,YAHOO.widget.TreeView.FOCUS_CLASS_NAME );
2513
+ }
2514
+ },
2515
+ /**
2516
+ * Sets the focus on the node element.
2517
+ * It will only be able to set the focus on nodes that have anchor elements in it.
2518
+ * Toggle or branch icons have anchors and can be focused on.
2519
+ * If will fail in nodes that have no anchor
2520
+ * @method focus
2521
+ * @return {boolean} success
2522
+ */
2523
+ focus: function () {
2524
+ return false; // foucs 抑制。
2525
+ var focused = false, self = this;
2526
+
2527
+ if (this.tree.currentFocus) {
2528
+ this.tree.currentFocus._removeFocus();
2529
+ }
2530
+
2531
+ var expandParent = function (node) {
2532
+ if (node.parent) {
2533
+ expandParent(node.parent);
2534
+ node.parent.expand();
2535
+ }
2536
+ };
2537
+ expandParent(this);
2538
+
2539
+ Dom.getElementsBy (
2540
+ function (el) {
2541
+ return /ygtv(([tl][pmn]h?)|(content))/.test(el.className);
2542
+ } ,
2543
+ 'td' ,
2544
+ self.getEl().firstChild ,
2545
+ function (el) {
2546
+ Dom.addClass(el, YAHOO.widget.TreeView.FOCUS_CLASS_NAME );
2547
+ if (!focused) {
2548
+ var aEl = el.getElementsByTagName('a');
2549
+ if (aEl.length) {
2550
+ aEl = aEl[0];
2551
+ aEl.focus();
2552
+ self._focusedItem = aEl;
2553
+ Event.on(aEl,'blur',function () {
2554
+ //console.log('f1');
2555
+ self.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null});
2556
+ self.tree.currentFocus = null;
2557
+ self._removeFocus();
2558
+ });
2559
+ focused = true;
2560
+ }
2561
+ }
2562
+ self._focusHighlightedItems.push(el);
2563
+ }
2564
+ );
2565
+ if (focused) {
2566
+ //console.log('f2');
2567
+ this.tree.fireEvent('focusChanged',{oldNode:this.tree.currentFocus,newNode:this});
2568
+ this.tree.currentFocus = this;
2569
+ } else {
2570
+ //console.log('f3');
2571
+ this.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null});
2572
+ this.tree.currentFocus = null;
2573
+ this._removeFocus();
2574
+ }
2575
+ return focused;
2576
+ },
2577
+
2578
+ /**
2579
+ * Count of nodes in a branch
2580
+ * @method getNodeCount
2581
+ * @return {int} number of nodes in the branch
2582
+ */
2583
+ getNodeCount: function() {
2584
+ for (var i = 0, count = 0;i< this.children.length;i++) {
2585
+ count += this.children[i].getNodeCount();
2586
+ }
2587
+ return count + 1;
2588
+ },
2589
+
2590
+ /**
2591
+ * Returns an object which could be used to build a tree out of this node and its children.
2592
+ * It can be passed to the tree constructor to reproduce this node as a tree.
2593
+ * It will return false if the node or any children loads dynamically, regardless of whether it is loaded or not.
2594
+ * @method getNodeDefinition
2595
+ * @return {Object | false} definition of the tree or false if the node or any children is defined as dynamic
2596
+ */
2597
+ getNodeDefinition: function() {
2598
+
2599
+ if (this.isDynamic()) { return false; }
2600
+
2601
+ var def, defs = Lang.merge(this.data), children = [];
2602
+
2603
+
2604
+
2605
+ if (this.expanded) {defs.expanded = this.expanded; }
2606
+ if (!this.multiExpand) { defs.multiExpand = this.multiExpand; }
2607
+ if (!this.renderHidden) { defs.renderHidden = this.renderHidden; }
2608
+ if (!this.hasIcon) { defs.hasIcon = this.hasIcon; }
2609
+ if (this.nowrap) { defs.nowrap = this.nowrap; }
2610
+ if (this.className) { defs.className = this.className; }
2611
+ if (this.editable) { defs.editable = this.editable; }
2612
+ if (this.enableHighlight) { defs.enableHighlight = this.enableHighlight; }
2613
+ if (this.highlightState) { defs.highlightState = this.highlightState; }
2614
+ if (this.propagateHighlightUp) { defs.propagateHighlightUp = this.propagateHighlightUp; }
2615
+ if (this.propagateHighlightDown) { defs.propagateHighlightDown = this.propagateHighlightDown; }
2616
+ defs.type = this._type;
2617
+
2618
+
2619
+
2620
+ for (var i = 0; i < this.children.length;i++) {
2621
+ def = this.children[i].getNodeDefinition();
2622
+ if (def === false) { return false;}
2623
+ children.push(def);
2624
+ }
2625
+ if (children.length) { defs.children = children; }
2626
+ return defs;
2627
+ },
2628
+
2629
+
2630
+ /**
2631
+ * Generates the link that will invoke this node's toggle method
2632
+ * @method getToggleLink
2633
+ * @return {string} the javascript url for toggling this node
2634
+ */
2635
+ getToggleLink: function() {
2636
+ return 'return false;';
2637
+ },
2638
+
2639
+ /**
2640
+ * Sets the value of property for this node and all loaded descendants.
2641
+ * Only public and defined properties can be set, not methods.
2642
+ * Values for unknown properties will be assigned to the refNode.data object
2643
+ * @method setNodesProperty
2644
+ * @param name {string} Name of the property to be set
2645
+ * @param value {any} value to be set
2646
+ * @param refresh {boolean} if present and true, it does a refresh
2647
+ */
2648
+ setNodesProperty: function(name, value, refresh) {
2649
+ if (name.charAt(0) != '_' && !Lang.isUndefined(this[name]) && !Lang.isFunction(this[name]) ) {
2650
+ this[name] = value;
2651
+ } else {
2652
+ this.data[name] = value;
2653
+ }
2654
+ for (var i = 0; i < this.children.length;i++) {
2655
+ this.children[i].setNodesProperty(name,value);
2656
+ }
2657
+ if (refresh) {
2658
+ this.refresh();
2659
+ }
2660
+ },
2661
+ /**
2662
+ * Toggles the highlighted state of a Node
2663
+ * @method toggleHighlight
2664
+ */
2665
+ toggleHighlight: function() {
2666
+ if (this.enableHighlight) {
2667
+ // unhighlights only if fully highligthed. For not or partially highlighted it will highlight
2668
+ if (this.highlightState == 1) {
2669
+ this.unhighlight();
2670
+ } else {
2671
+ this.highlight();
2672
+ }
2673
+ }
2674
+ },
2675
+
2676
+ /**
2677
+ * Turns highlighting on node.
2678
+ * @method highlight
2679
+ * @param _silent {boolean} optional, don't fire the highlightEvent
2680
+ */
2681
+ highlight: function(_silent) {
2682
+ if (this.enableHighlight) {
2683
+ if (this.tree.singleNodeHighlight) {
2684
+ if (this.tree._currentlyHighlighted) {
2685
+ this.tree._currentlyHighlighted.unhighlight();
2686
+ }
2687
+ this.tree._currentlyHighlighted = this;
2688
+ }
2689
+ this.highlightState = 1;
2690
+ this._setHighlightClassName();
2691
+ if (this.propagateHighlightDown) {
2692
+ for (var i = 0;i < this.children.length;i++) {
2693
+ this.children[i].highlight(true);
2694
+ }
2695
+ }
2696
+ if (this.propagateHighlightUp) {
2697
+ if (this.parent) {
2698
+ this.parent._childrenHighlighted();
2699
+ }
2700
+ }
2701
+ if (!_silent) {
2702
+ this.tree.fireEvent('highlightEvent',this);
2703
+ }
2704
+ }
2705
+ },
2706
+ /**
2707
+ * Turns highlighting off a node.
2708
+ * @method unhighlight
2709
+ * @param _silent {boolean} optional, don't fire the highlightEvent
2710
+ */
2711
+ unhighlight: function(_silent) {
2712
+ if (this.enableHighlight) {
2713
+ this.highlightState = 0;
2714
+ this._setHighlightClassName();
2715
+ if (this.propagateHighlightDown) {
2716
+ for (var i = 0;i < this.children.length;i++) {
2717
+ this.children[i].unhighlight(true);
2718
+ }
2719
+ }
2720
+ if (this.propagateHighlightUp) {
2721
+ if (this.parent) {
2722
+ this.parent._childrenHighlighted();
2723
+ }
2724
+ }
2725
+ if (!_silent) {
2726
+ this.tree.fireEvent('highlightEvent',this);
2727
+ }
2728
+ }
2729
+ },
2730
+ /**
2731
+ * Checks whether all or part of the children of a node are highlighted and
2732
+ * sets the node highlight to full, none or partial highlight.
2733
+ * If set to propagate it will further call the parent
2734
+ * @method _childrenHighlighted
2735
+ * @private
2736
+ */
2737
+ _childrenHighlighted: function() {
2738
+ var yes = false, no = false;
2739
+ if (this.enableHighlight) {
2740
+ for (var i = 0;i < this.children.length;i++) {
2741
+ switch(this.children[i].highlightState) {
2742
+ case 0:
2743
+ no = true;
2744
+ break;
2745
+ case 1:
2746
+ yes = true;
2747
+ break;
2748
+ case 2:
2749
+ yes = no = true;
2750
+ break;
2751
+ }
2752
+ }
2753
+ if (yes && no) {
2754
+ this.highlightState = 2;
2755
+ } else if (yes) {
2756
+ this.highlightState = 1;
2757
+ } else {
2758
+ this.highlightState = 0;
2759
+ }
2760
+ this._setHighlightClassName();
2761
+ if (this.propagateHighlightUp) {
2762
+ if (this.parent) {
2763
+ this.parent._childrenHighlighted();
2764
+ }
2765
+ }
2766
+ }
2767
+ },
2768
+
2769
+ /**
2770
+ * Changes the classNames on the toggle and content containers to reflect the current highlighting
2771
+ * @method _setHighlightClassName
2772
+ * @private
2773
+ */
2774
+ _setHighlightClassName: function() {
2775
+ var el = Dom.get('ygtvtableel' + this.index);
2776
+ if (el) {
2777
+ el.className = el.className.replace(/\bygtv-highlight\d\b/gi,'ygtv-highlight' + this.highlightState);
2778
+ }
2779
+ }
2780
+
2781
+ };
2782
+
2783
+ YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider);
2784
+ })();
2785
+ /**
2786
+ * A custom YAHOO.widget.Node that handles the unique nature of
2787
+ * the virtual, presentationless root node.
2788
+ * @namespace YAHOO.widget
2789
+ * @class RootNode
2790
+ * @extends YAHOO.widget.Node
2791
+ * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to
2792
+ * @constructor
2793
+ */
2794
+ YAHOO.widget.RootNode = function(oTree) {
2795
+ // Initialize the node with null params. The root node is a
2796
+ // special case where the node has no presentation. So we have
2797
+ // to alter the standard properties a bit.
2798
+ this.init(null, null, true);
2799
+
2800
+ /*
2801
+ * For the root node, we get the tree reference from as a param
2802
+ * to the constructor instead of from the parent element.
2803
+ */
2804
+ this.tree = oTree;
2805
+ };
2806
+
2807
+ YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, {
2808
+
2809
+ /**
2810
+ * The node type
2811
+ * @property _type
2812
+ * @type string
2813
+ * @private
2814
+ * @default "RootNode"
2815
+ */
2816
+ _type: "RootNode",
2817
+
2818
+ // overrides YAHOO.widget.Node
2819
+ getNodeHtml: function() {
2820
+ return "";
2821
+ },
2822
+
2823
+ toString: function() {
2824
+ return this._type;
2825
+ },
2826
+
2827
+ loadComplete: function() {
2828
+ this.tree.draw();
2829
+ },
2830
+
2831
+ /**
2832
+ * Count of nodes in tree.
2833
+ * It overrides Nodes.getNodeCount because the root node should not be counted.
2834
+ * @method getNodeCount
2835
+ * @return {int} number of nodes in the tree
2836
+ */
2837
+ getNodeCount: function() {
2838
+ for (var i = 0, count = 0;i< this.children.length;i++) {
2839
+ count += this.children[i].getNodeCount();
2840
+ }
2841
+ return count;
2842
+ },
2843
+
2844
+ /**
2845
+ * Returns an object which could be used to build a tree out of this node and its children.
2846
+ * It can be passed to the tree constructor to reproduce this node as a tree.
2847
+ * Since the RootNode is automatically created by treeView,
2848
+ * its own definition is excluded from the returned node definition
2849
+ * which only contains its children.
2850
+ * @method getNodeDefinition
2851
+ * @return {Object | false} definition of the tree or false if any child node is defined as dynamic
2852
+ */
2853
+ getNodeDefinition: function() {
2854
+
2855
+ for (var def, defs = [], i = 0; i < this.children.length;i++) {
2856
+ def = this.children[i].getNodeDefinition();
2857
+ if (def === false) { return false;}
2858
+ defs.push(def);
2859
+ }
2860
+ return defs;
2861
+ },
2862
+
2863
+ collapse: function() {},
2864
+ expand: function() {},
2865
+ getSiblings: function() { return null; },
2866
+ focus: function () {}
2867
+
2868
+ });
2869
+ (function () {
2870
+ var Dom = YAHOO.util.Dom,
2871
+ Lang = YAHOO.lang,
2872
+ Event = YAHOO.util.Event;
2873
+ /**
2874
+ * The default node presentation. The first parameter should be
2875
+ * either a string that will be used as the node's label, or an object
2876
+ * that has at least a string property called label. By default, clicking the
2877
+ * label will toggle the expanded/collapsed state of the node. By
2878
+ * setting the href property of the instance, this behavior can be
2879
+ * changed so that the label will go to the specified href.
2880
+ * @namespace YAHOO.widget
2881
+ * @class TextNode
2882
+ * @extends YAHOO.widget.Node
2883
+ * @constructor
2884
+ * @param oData {object} a string or object containing the data that will
2885
+ * be used to render this node.
2886
+ * Providing a string is the same as providing an object with a single property named label.
2887
+ * All values in the oData will be used to set equally named properties in the node
2888
+ * as long as the node does have such properties, they are not undefined, private or functions.
2889
+ * All attributes are made available in noderef.data, which
2890
+ * can be used to store custom attributes. TreeView.getNode(s)ByProperty
2891
+ * can be used to retrieve a node by one of the attributes.
2892
+ * @param oParent {YAHOO.widget.Node} this node's parent node
2893
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
2894
+ */
2895
+ YAHOO.widget.TextNode = function(oData, oParent, expanded) {
2896
+
2897
+ if (oData) {
2898
+ if (Lang.isString(oData)) {
2899
+ oData = { label: oData };
2900
+ }
2901
+ this.init(oData, oParent, expanded);
2902
+ this.setUpLabel(oData);
2903
+ }
2904
+
2905
+ };
2906
+
2907
+ YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, {
2908
+
2909
+ /**
2910
+ * The CSS class for the label href. Defaults to ygtvlabel, but can be
2911
+ * overridden to provide a custom presentation for a specific node.
2912
+ * @property labelStyle
2913
+ * @type string
2914
+ */
2915
+ labelStyle: "ygtvlabel",
2916
+
2917
+ /**
2918
+ * The derived element id of the label for this node
2919
+ * @property labelElId
2920
+ * @type string
2921
+ */
2922
+ labelElId: null,
2923
+
2924
+ /**
2925
+ * The text for the label. It is assumed that the oData parameter will
2926
+ * either be a string that will be used as the label, or an object that
2927
+ * has a property called "label" that we will use.
2928
+ * @property label
2929
+ * @type string
2930
+ */
2931
+ label: null,
2932
+
2933
+ /**
2934
+ * The text for the title (tooltip) for the label element
2935
+ * @property title
2936
+ * @type string
2937
+ */
2938
+ title: null,
2939
+
2940
+ /**
2941
+ * The href for the node's label. If one is not specified, the href will
2942
+ * be set so that it toggles the node.
2943
+ * @property href
2944
+ * @type string
2945
+ */
2946
+ href: null,
2947
+
2948
+ /**
2949
+ * The label href target, defaults to current window
2950
+ * @property target
2951
+ * @type string
2952
+ */
2953
+ target: "_self",
2954
+
2955
+ /**
2956
+ * The node type
2957
+ * @property _type
2958
+ * @private
2959
+ * @type string
2960
+ * @default "TextNode"
2961
+ */
2962
+ _type: "TextNode",
2963
+
2964
+
2965
+ /**
2966
+ * Sets up the node label
2967
+ * @method setUpLabel
2968
+ * @param oData string containing the label, or an object with a label property
2969
+ */
2970
+ setUpLabel: function(oData) {
2971
+
2972
+ if (Lang.isString(oData)) {
2973
+ oData = {
2974
+ label: oData
2975
+ };
2976
+ } else {
2977
+ if (oData.style) {
2978
+ this.labelStyle = oData.style;
2979
+ }
2980
+ }
2981
+
2982
+ this.label = oData.label;
2983
+
2984
+ this.labelElId = "ygtvlabelel" + this.index;
2985
+
2986
+ },
2987
+
2988
+ /**
2989
+ * Returns the label element
2990
+ * @for YAHOO.widget.TextNode
2991
+ * @method getLabelEl
2992
+ * @return {object} the element
2993
+ */
2994
+ getLabelEl: function() {
2995
+ return Dom.get(this.labelElId);
2996
+ },
2997
+
2998
+ // overrides YAHOO.widget.Node
2999
+ getContentHtml: function() {
3000
+ var sb = [];
3001
+ sb[sb.length] = this.href?'<a':'<span';
3002
+ sb[sb.length] = ' id="' + this.labelElId + '"';
3003
+ sb[sb.length] = ' class="' + this.labelStyle + '"';
3004
+ if (this.href) {
3005
+ sb[sb.length] = ' href="' + this.href + '"';
3006
+ sb[sb.length] = ' target="' + this.target + '"';
3007
+ }
3008
+ if (this.title) {
3009
+ sb[sb.length] = ' title="' + this.title + '"';
3010
+ }
3011
+ sb[sb.length] = ' >';
3012
+ sb[sb.length] = this.label;
3013
+ sb[sb.length] = this.href?'</a>':'</span>';
3014
+ return sb.join("");
3015
+ },
3016
+
3017
+
3018
+
3019
+ /**
3020
+ * Returns an object which could be used to build a tree out of this node and its children.
3021
+ * It can be passed to the tree constructor to reproduce this node as a tree.
3022
+ * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not.
3023
+ * @method getNodeDefinition
3024
+ * @return {Object | false} definition of the tree or false if this node or any descendant is defined as dynamic
3025
+ */
3026
+ getNodeDefinition: function() {
3027
+ var def = YAHOO.widget.TextNode.superclass.getNodeDefinition.call(this);
3028
+ if (def === false) { return false; }
3029
+
3030
+ // Node specific properties
3031
+ def.label = this.label;
3032
+ if (this.labelStyle != 'ygtvlabel') { def.style = this.labelStyle; }
3033
+ if (this.title) { def.title = this.title; }
3034
+ if (this.href) { def.href = this.href; }
3035
+ if (this.target != '_self') { def.target = this.target; }
3036
+
3037
+ return def;
3038
+
3039
+ },
3040
+
3041
+ toString: function() {
3042
+ return YAHOO.widget.TextNode.superclass.toString.call(this) + ": " + this.label;
3043
+ },
3044
+
3045
+ // deprecated
3046
+ onLabelClick: function() {
3047
+ return false;
3048
+ },
3049
+ refresh: function() {
3050
+ YAHOO.widget.TextNode.superclass.refresh.call(this);
3051
+ var label = this.getLabelEl();
3052
+ label.innerHTML = this.label;
3053
+ if (label.tagName.toUpperCase() == 'A') {
3054
+ label.href = this.href;
3055
+ label.target = this.target;
3056
+ }
3057
+ }
3058
+
3059
+
3060
+
3061
+
3062
+ });
3063
+ })();
3064
+ /**
3065
+ * A menu-specific implementation that differs from TextNode in that only
3066
+ * one sibling can be expanded at a time.
3067
+ * @namespace YAHOO.widget
3068
+ * @class MenuNode
3069
+ * @extends YAHOO.widget.TextNode
3070
+ * @param oData {object} a string or object containing the data that will
3071
+ * be used to render this node.
3072
+ * Providing a string is the same as providing an object with a single property named label.
3073
+ * All values in the oData will be used to set equally named properties in the node
3074
+ * as long as the node does have such properties, they are not undefined, private or functions.
3075
+ * All attributes are made available in noderef.data, which
3076
+ * can be used to store custom attributes. TreeView.getNode(s)ByProperty
3077
+ * can be used to retrieve a node by one of the attributes.
3078
+ * @param oParent {YAHOO.widget.Node} this node's parent node
3079
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
3080
+ * @constructor
3081
+ */
3082
+ YAHOO.widget.MenuNode = function(oData, oParent, expanded) {
3083
+ YAHOO.widget.MenuNode.superclass.constructor.call(this,oData,oParent,expanded);
3084
+
3085
+ /*
3086
+ * Menus usually allow only one branch to be open at a time.
3087
+ */
3088
+ this.multiExpand = false;
3089
+
3090
+ };
3091
+
3092
+ YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, {
3093
+
3094
+ /**
3095
+ * The node type
3096
+ * @property _type
3097
+ * @private
3098
+ * @default "MenuNode"
3099
+ */
3100
+ _type: "MenuNode"
3101
+
3102
+ });
3103
+ (function () {
3104
+ var Dom = YAHOO.util.Dom,
3105
+ Lang = YAHOO.lang,
3106
+ Event = YAHOO.util.Event;
3107
+
3108
+ /**
3109
+ * This implementation takes either a string or object for the
3110
+ * oData argument. If is it a string, it will use it for the display
3111
+ * of this node (and it can contain any html code). If the parameter
3112
+ * is an object,it looks for a parameter called "html" that will be
3113
+ * used for this node's display.
3114
+ * @namespace YAHOO.widget
3115
+ * @class HTMLNode
3116
+ * @extends YAHOO.widget.Node
3117
+ * @constructor
3118
+ * @param oData {object} a string or object containing the data that will
3119
+ * be used to render this node.
3120
+ * Providing a string is the same as providing an object with a single property named html.
3121
+ * All values in the oData will be used to set equally named properties in the node
3122
+ * as long as the node does have such properties, they are not undefined, private or functions.
3123
+ * All other attributes are made available in noderef.data, which
3124
+ * can be used to store custom attributes. TreeView.getNode(s)ByProperty
3125
+ * can be used to retrieve a node by one of the attributes.
3126
+ * @param oParent {YAHOO.widget.Node} this node's parent node
3127
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
3128
+ * @param hasIcon {boolean} specifies whether or not leaf nodes should
3129
+ * be rendered with or without a horizontal line line and/or toggle icon. If the icon
3130
+ * is not displayed, the content fills the space it would have occupied.
3131
+ * This option operates independently of the leaf node presentation logic
3132
+ * for dynamic nodes.
3133
+ * (deprecated; use oData.hasIcon)
3134
+ */
3135
+ YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) {
3136
+ if (oData) {
3137
+ this.init(oData, oParent, expanded);
3138
+ this.initContent(oData, hasIcon);
3139
+ }
3140
+ };
3141
+
3142
+ YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, {
3143
+
3144
+ /**
3145
+ * The CSS class for the html content container. Defaults to ygtvhtml, but
3146
+ * can be overridden to provide a custom presentation for a specific node.
3147
+ * @property contentStyle
3148
+ * @type string
3149
+ */
3150
+ contentStyle: "ygtvhtml",
3151
+
3152
+
3153
+ /**
3154
+ * The HTML content to use for this node's display
3155
+ * @property html
3156
+ * @type string
3157
+ */
3158
+ html: null,
3159
+
3160
+ /**
3161
+ * The node type
3162
+ * @property _type
3163
+ * @private
3164
+ * @type string
3165
+ * @default "HTMLNode"
3166
+ */
3167
+ _type: "HTMLNode",
3168
+
3169
+ /**
3170
+ * Sets up the node label
3171
+ * @property initContent
3172
+ * @param oData {object} An html string or object containing an html property
3173
+ * @param hasIcon {boolean} determines if the node will be rendered with an
3174
+ * icon or not
3175
+ */
3176
+ initContent: function(oData, hasIcon) {
3177
+ this.setHtml(oData);
3178
+ this.contentElId = "ygtvcontentel" + this.index;
3179
+ if (!Lang.isUndefined(hasIcon)) { this.hasIcon = hasIcon; }
3180
+
3181
+ },
3182
+
3183
+ /**
3184
+ * Synchronizes the node.data, node.html, and the node's content
3185
+ * @property setHtml
3186
+ * @param o {object} An html string or object containing an html property
3187
+ */
3188
+ setHtml: function(o) {
3189
+
3190
+ this.html = (typeof o === "string") ? o : o.html;
3191
+
3192
+ var el = this.getContentEl();
3193
+ if (el) {
3194
+ el.innerHTML = this.html;
3195
+ }
3196
+
3197
+ },
3198
+
3199
+ // overrides YAHOO.widget.Node
3200
+ getContentHtml: function() {
3201
+ return this.html;
3202
+ },
3203
+
3204
+ /**
3205
+ * Returns an object which could be used to build a tree out of this node and its children.
3206
+ * It can be passed to the tree constructor to reproduce this node as a tree.
3207
+ * It will return false if any node loads dynamically, regardless of whether it is loaded or not.
3208
+ * @method getNodeDefinition
3209
+ * @return {Object | false} definition of the tree or false if any node is defined as dynamic
3210
+ */
3211
+ getNodeDefinition: function() {
3212
+ var def = YAHOO.widget.HTMLNode.superclass.getNodeDefinition.call(this);
3213
+ if (def === false) { return false; }
3214
+ def.html = this.html;
3215
+ return def;
3216
+
3217
+ }
3218
+ });
3219
+ })();
3220
+ (function () {
3221
+ var Dom = YAHOO.util.Dom,
3222
+ Lang = YAHOO.lang,
3223
+ Event = YAHOO.util.Event,
3224
+ Calendar = YAHOO.widget.Calendar;
3225
+
3226
+ /**
3227
+ * A Date-specific implementation that differs from TextNode in that it uses
3228
+ * YAHOO.widget.Calendar as an in-line editor, if available
3229
+ * If Calendar is not available, it behaves as a plain TextNode.
3230
+ * @namespace YAHOO.widget
3231
+ * @class DateNode
3232
+ * @extends YAHOO.widget.TextNode
3233
+ * @param oData {object} a string or object containing the data that will
3234
+ * be used to render this node.
3235
+ * Providing a string is the same as providing an object with a single property named label.
3236
+ * All values in the oData will be used to set equally named properties in the node
3237
+ * as long as the node does have such properties, they are not undefined, private nor functions.
3238
+ * All attributes are made available in noderef.data, which
3239
+ * can be used to store custom attributes. TreeView.getNode(s)ByProperty
3240
+ * can be used to retrieve a node by one of the attributes.
3241
+ * @param oParent {YAHOO.widget.Node} this node's parent node
3242
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
3243
+ * @constructor
3244
+ */
3245
+ YAHOO.widget.DateNode = function(oData, oParent, expanded) {
3246
+ YAHOO.widget.DateNode.superclass.constructor.call(this,oData, oParent, expanded);
3247
+ };
3248
+
3249
+ YAHOO.extend(YAHOO.widget.DateNode, YAHOO.widget.TextNode, {
3250
+
3251
+ /**
3252
+ * The node type
3253
+ * @property _type
3254
+ * @type string
3255
+ * @private
3256
+ * @default "DateNode"
3257
+ */
3258
+ _type: "DateNode",
3259
+
3260
+ /**
3261
+ * Configuration object for the Calendar editor, if used.
3262
+ * See <a href="http://developer.yahoo.com/yui/calendar/#internationalization">http://developer.yahoo.com/yui/calendar/#internationalization</a>
3263
+ * @property calendarConfig
3264
+ */
3265
+ calendarConfig: null,
3266
+
3267
+
3268
+
3269
+ /**
3270
+ * If YAHOO.widget.Calendar is available, it will pop up a Calendar to enter a new date. Otherwise, it falls back to a plain &lt;input&gt; textbox
3271
+ * @method fillEditorContainer
3272
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3273
+ * @return void
3274
+ */
3275
+ fillEditorContainer: function (editorData) {
3276
+
3277
+ var cal, container = editorData.inputContainer;
3278
+
3279
+ if (Lang.isUndefined(Calendar)) {
3280
+ Dom.replaceClass(editorData.editorPanel,'ygtv-edit-DateNode','ygtv-edit-TextNode');
3281
+ YAHOO.widget.DateNode.superclass.fillEditorContainer.call(this, editorData);
3282
+ return;
3283
+ }
3284
+
3285
+ if (editorData.nodeType != this._type) {
3286
+ editorData.nodeType = this._type;
3287
+ editorData.saveOnEnter = false;
3288
+
3289
+ editorData.node.destroyEditorContents(editorData);
3290
+
3291
+ editorData.inputObject = cal = new Calendar(container.appendChild(document.createElement('div')));
3292
+ if (this.calendarConfig) {
3293
+ cal.cfg.applyConfig(this.calendarConfig,true);
3294
+ cal.cfg.fireQueue();
3295
+ }
3296
+ cal.selectEvent.subscribe(function () {
3297
+ this.tree._closeEditor(true);
3298
+ },this,true);
3299
+ } else {
3300
+ cal = editorData.inputObject;
3301
+ }
3302
+
3303
+ cal.cfg.setProperty("selected",this.label, false);
3304
+
3305
+ var delim = cal.cfg.getProperty('DATE_FIELD_DELIMITER');
3306
+ var pageDate = this.label.split(delim);
3307
+ cal.cfg.setProperty('pagedate',pageDate[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] + delim + pageDate[cal.cfg.getProperty('MDY_YEAR_POSITION') -1]);
3308
+ cal.cfg.fireQueue();
3309
+
3310
+ cal.render();
3311
+ cal.oDomContainer.focus();
3312
+ },
3313
+ /**
3314
+ * Saves the date entered in the editor into the DateNode label property and displays it.
3315
+ * Overrides Node.saveEditorValue
3316
+ * @method saveEditorValue
3317
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3318
+ */
3319
+ saveEditorValue: function (editorData) {
3320
+ var node = editorData.node,
3321
+ validator = node.tree.validator,
3322
+ value;
3323
+ if (Lang.isUndefined(Calendar)) {
3324
+ value = editorData.inputElement.value;
3325
+ } else {
3326
+ var cal = editorData.inputObject,
3327
+ date = cal.getSelectedDates()[0],
3328
+ dd = [];
3329
+
3330
+ dd[cal.cfg.getProperty('MDY_DAY_POSITION') -1] = date.getDate();
3331
+ dd[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] = date.getMonth() + 1;
3332
+ dd[cal.cfg.getProperty('MDY_YEAR_POSITION') -1] = date.getFullYear();
3333
+ value = dd.join(cal.cfg.getProperty('DATE_FIELD_DELIMITER'));
3334
+ }
3335
+ if (Lang.isFunction(validator)) {
3336
+ value = validator(value,node.label,node);
3337
+ if (Lang.isUndefined(value)) { return false; }
3338
+ }
3339
+
3340
+ node.label = value;
3341
+ node.getLabelEl().innerHTML = value;
3342
+ },
3343
+ /**
3344
+ * Returns an object which could be used to build a tree out of this node and its children.
3345
+ * It can be passed to the tree constructor to reproduce this node as a tree.
3346
+ * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not.
3347
+ * @method getNodeDefinition
3348
+ * @return {Object | false} definition of the node or false if this node or any descendant is defined as dynamic
3349
+ */
3350
+ getNodeDefinition: function() {
3351
+ var def = YAHOO.widget.DateNode.superclass.getNodeDefinition.call(this);
3352
+ if (def === false) { return false; }
3353
+ if (this.calendarConfig) { def.calendarConfig = this.calendarConfig; }
3354
+ return def;
3355
+ }
3356
+
3357
+
3358
+ });
3359
+ })();
3360
+ (function () {
3361
+ var Dom = YAHOO.util.Dom,
3362
+ Lang = YAHOO.lang,
3363
+ Event = YAHOO.util.Event,
3364
+ TV = YAHOO.widget.TreeView,
3365
+ TVproto = TV.prototype;
3366
+
3367
+ /**
3368
+ * An object to store information used for in-line editing
3369
+ * for all Nodes of all TreeViews. It contains:
3370
+ * <ul>
3371
+ * <li>active {boolean}, whether there is an active cell editor </li>
3372
+ * <li>whoHasIt {YAHOO.widget.TreeView} TreeView instance that is currently using the editor</li>
3373
+ * <li>nodeType {string} value of static Node._type property, allows reuse of input element if node is of the same type.</li>
3374
+ * <li>editorPanel {HTMLelement (&lt;div&gt;)} element holding the in-line editor</li>
3375
+ * <li>inputContainer {HTMLelement (&lt;div&gt;)} element which will hold the type-specific input element(s) to be filled by the fillEditorContainer method</li>
3376
+ * <li>buttonsContainer {HTMLelement (&lt;div&gt;)} element which holds the &lt;button&gt; elements for Ok/Cancel. If you don't want any of the buttons, hide it via CSS styles, don't destroy it</li>
3377
+ * <li>node {YAHOO.widget.Node} reference to the Node being edited</li>
3378
+ * <li>saveOnEnter {boolean}, whether the Enter key should be accepted as a Save command (Esc. is always taken as Cancel), disable for multi-line input elements </li>
3379
+ * </ul>
3380
+ * Editors are free to use this object to store additional data.
3381
+ * @property editorData
3382
+ * @static
3383
+ * @for YAHOO.widget.TreeView
3384
+ */
3385
+ TV.editorData = {
3386
+ active:false,
3387
+ whoHasIt:null, // which TreeView has it
3388
+ nodeType:null,
3389
+ editorPanel:null,
3390
+ inputContainer:null,
3391
+ buttonsContainer:null,
3392
+ node:null, // which Node is being edited
3393
+ saveOnEnter:true
3394
+ // Each node type is free to add its own properties to this as it sees fit.
3395
+ };
3396
+
3397
+ /**
3398
+ * Validator function for edited data, called from the TreeView instance scope,
3399
+ * receives the arguments (newValue, oldValue, nodeInstance)
3400
+ * and returns either the validated (or type-converted) value or undefined.
3401
+ * An undefined return will prevent the editor from closing
3402
+ * @property validator
3403
+ * @default null
3404
+ * @for YAHOO.widget.TreeView
3405
+ */
3406
+ TVproto.validator = null;
3407
+
3408
+ /**
3409
+ * Entry point of the editing plug-in.
3410
+ * TreeView will call this method if it exists when a node label is clicked
3411
+ * @method _nodeEditing
3412
+ * @param node {YAHOO.widget.Node} the node to be edited
3413
+ * @return {Boolean} true to indicate that the node is editable and prevent any further bubbling of the click.
3414
+ * @for YAHOO.widget.TreeView
3415
+ * @private
3416
+ */
3417
+
3418
+
3419
+ TVproto._nodeEditing = function (node) {
3420
+ if (node.fillEditorContainer && node.editable) {
3421
+ var ed, topLeft, buttons, button, editorData = TV.editorData;
3422
+ editorData.active = true;
3423
+ editorData.whoHasIt = this;
3424
+ if (!editorData.nodeType) {
3425
+ editorData.editorPanel = ed = document.body.appendChild(document.createElement('div'));
3426
+ Dom.addClass(ed,'ygtv-label-editor');
3427
+
3428
+ buttons = editorData.buttonsContainer = ed.appendChild(document.createElement('div'));
3429
+ Dom.addClass(buttons,'ygtv-button-container');
3430
+ button = buttons.appendChild(document.createElement('button'));
3431
+ Dom.addClass(button,'ygtvok');
3432
+ button.innerHTML = ' ';
3433
+ button = buttons.appendChild(document.createElement('button'));
3434
+ Dom.addClass(button,'ygtvcancel');
3435
+ button.innerHTML = ' ';
3436
+ Event.on(buttons, 'click', function (ev) {
3437
+ var target = Event.getTarget(ev);
3438
+ var node = TV.editorData.node;
3439
+ if (Dom.hasClass(target,'ygtvok')) {
3440
+ Event.stopEvent(ev);
3441
+ this._closeEditor(true);
3442
+ }
3443
+ if (Dom.hasClass(target,'ygtvcancel')) {
3444
+ Event.stopEvent(ev);
3445
+ this._closeEditor(false);
3446
+ }
3447
+ }, this, true);
3448
+
3449
+ editorData.inputContainer = ed.appendChild(document.createElement('div'));
3450
+ Dom.addClass(editorData.inputContainer,'ygtv-input');
3451
+
3452
+ Event.on(ed,'keydown',function (ev) {
3453
+ var editorData = TV.editorData,
3454
+ KEY = YAHOO.util.KeyListener.KEY;
3455
+ switch (ev.keyCode) {
3456
+ case KEY.ENTER:
3457
+ Event.stopEvent(ev);
3458
+ if (editorData.saveOnEnter) {
3459
+ this._closeEditor(true);
3460
+ }
3461
+ break;
3462
+ case KEY.ESCAPE:
3463
+ Event.stopEvent(ev);
3464
+ this._closeEditor(false);
3465
+ break;
3466
+ }
3467
+ },this,true);
3468
+
3469
+
3470
+
3471
+ } else {
3472
+ ed = editorData.editorPanel;
3473
+ }
3474
+ editorData.node = node;
3475
+ if (editorData.nodeType) {
3476
+ Dom.removeClass(ed,'ygtv-edit-' + editorData.nodeType);
3477
+ }
3478
+ Dom.addClass(ed,' ygtv-edit-' + node._type);
3479
+ topLeft = Dom.getXY(node.getContentEl());
3480
+ Dom.setStyle(ed,'left',topLeft[0] + 'px');
3481
+ Dom.setStyle(ed,'top',topLeft[1] + 'px');
3482
+ Dom.setStyle(ed,'display','block');
3483
+ ed.focus();
3484
+ node.fillEditorContainer(editorData);
3485
+
3486
+ return true; // If inline editor available, don't do anything else.
3487
+ }
3488
+ };
3489
+
3490
+ /**
3491
+ * Method to be associated with an event (clickEvent, dblClickEvent or enterKeyPressed) to pop up the contents editor
3492
+ * It calls the corresponding node editNode method.
3493
+ * @method onEventEditNode
3494
+ * @param oArgs {object} Object passed as arguments to TreeView event listeners
3495
+ * @for YAHOO.widget.TreeView
3496
+ */
3497
+
3498
+ TVproto.onEventEditNode = function (oArgs) {
3499
+ if (oArgs instanceof YAHOO.widget.Node) {
3500
+ oArgs.editNode();
3501
+ } else if (oArgs.node instanceof YAHOO.widget.Node) {
3502
+ oArgs.node.editNode();
3503
+ }
3504
+ };
3505
+
3506
+ /**
3507
+ * Method to be called when the inline editing is finished and the editor is to be closed
3508
+ * @method _closeEditor
3509
+ * @param save {Boolean} true if the edited value is to be saved, false if discarded
3510
+ * @private
3511
+ * @for YAHOO.widget.TreeView
3512
+ */
3513
+
3514
+ TVproto._closeEditor = function (save) {
3515
+ var ed = TV.editorData,
3516
+ node = ed.node,
3517
+ close = true;
3518
+ if (save) {
3519
+ close = ed.node.saveEditorValue(ed) !== false;
3520
+ }
3521
+ if (close) {
3522
+ Dom.setStyle(ed.editorPanel,'display','none');
3523
+ ed.active = false;
3524
+ node.focus();
3525
+ }
3526
+ };
3527
+
3528
+ /**
3529
+ * Entry point for TreeView's destroy method to destroy whatever the editing plug-in has created
3530
+ * @method _destroyEditor
3531
+ * @private
3532
+ * @for YAHOO.widget.TreeView
3533
+ */
3534
+ TVproto._destroyEditor = function() {
3535
+ var ed = TV.editorData;
3536
+ if (ed && ed.nodeType && (!ed.active || ed.whoHasIt === this)) {
3537
+ Event.removeListener(ed.editorPanel,'keydown');
3538
+ Event.removeListener(ed.buttonContainer,'click');
3539
+ ed.node.destroyEditorContents(ed);
3540
+ document.body.removeChild(ed.editorPanel);
3541
+ ed.nodeType = ed.editorPanel = ed.inputContainer = ed.buttonsContainer = ed.whoHasIt = ed.node = null;
3542
+ ed.active = false;
3543
+ }
3544
+ };
3545
+
3546
+ var Nproto = YAHOO.widget.Node.prototype;
3547
+
3548
+ /**
3549
+ * Signals if the label is editable. (Ignored on TextNodes with href set.)
3550
+ * @property editable
3551
+ * @type boolean
3552
+ * @for YAHOO.widget.Node
3553
+ */
3554
+ Nproto.editable = false;
3555
+
3556
+ /**
3557
+ * pops up the contents editor, if there is one and the node is declared editable
3558
+ * @method editNode
3559
+ * @for YAHOO.widget.Node
3560
+ */
3561
+
3562
+ Nproto.editNode = function () {
3563
+ this.tree._nodeEditing(this);
3564
+ };
3565
+
3566
+
3567
+
3568
+
3569
+ /** Placeholder for a function that should provide the inline node label editor.
3570
+ * Leaving it set to null will indicate that this node type is not editable.
3571
+ * It should be overridden by nodes that provide inline editing.
3572
+ * The Node-specific editing element (input box, textarea or whatever) should be inserted into editorData.inputContainer.
3573
+ * @method fillEditorContainer
3574
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3575
+ * @return void
3576
+ * @for YAHOO.widget.Node
3577
+ */
3578
+ Nproto.fillEditorContainer = null;
3579
+
3580
+
3581
+ /**
3582
+ * Node-specific destroy function to empty the contents of the inline editor panel
3583
+ * This function is the worst case alternative that will purge all possible events and remove the editor contents
3584
+ * Method Event.purgeElement is somewhat costly so if it can be replaced by specifc Event.removeListeners, it is better to do so.
3585
+ * @method destroyEditorContents
3586
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3587
+ * @for YAHOO.widget.Node
3588
+ */
3589
+ Nproto.destroyEditorContents = function (editorData) {
3590
+ // In the worst case, if the input editor (such as the Calendar) has no destroy method
3591
+ // we can only try to remove all possible events on it.
3592
+ Event.purgeElement(editorData.inputContainer,true);
3593
+ editorData.inputContainer.innerHTML = '';
3594
+ };
3595
+
3596
+ /**
3597
+ * Saves the value entered into the editor.
3598
+ * Should be overridden by each node type
3599
+ * @method saveEditorValue
3600
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3601
+ * @return a return of exactly false will prevent the editor from closing
3602
+ * @for YAHOO.widget.Node
3603
+ */
3604
+ Nproto.saveEditorValue = function (editorData) {
3605
+ };
3606
+
3607
+ var TNproto = YAHOO.widget.TextNode.prototype;
3608
+
3609
+
3610
+
3611
+ /**
3612
+ * Places an &lt;input&gt; textbox in the input container and loads the label text into it
3613
+ * @method fillEditorContainer
3614
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3615
+ * @return void
3616
+ * @for YAHOO.widget.TextNode
3617
+ */
3618
+ TNproto.fillEditorContainer = function (editorData) {
3619
+
3620
+ var input;
3621
+ // If last node edited is not of the same type as this one, delete it and fill it with our editor
3622
+ if (editorData.nodeType != this._type) {
3623
+ editorData.nodeType = this._type;
3624
+ editorData.saveOnEnter = true;
3625
+ editorData.node.destroyEditorContents(editorData);
3626
+
3627
+ editorData.inputElement = input = editorData.inputContainer.appendChild(document.createElement('input'));
3628
+
3629
+ } else {
3630
+ // if the last node edited was of the same time, reuse the input element.
3631
+ input = editorData.inputElement;
3632
+ }
3633
+
3634
+ input.value = this.label;
3635
+ input.focus();
3636
+ input.select();
3637
+ };
3638
+
3639
+ /**
3640
+ * Saves the value entered in the editor into the TextNode label property and displays it
3641
+ * Overrides Node.saveEditorValue
3642
+ * @method saveEditorValue
3643
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3644
+ * @for YAHOO.widget.TextNode
3645
+ */
3646
+ TNproto.saveEditorValue = function (editorData) {
3647
+ var node = editorData.node,
3648
+ value = editorData.inputElement.value,
3649
+ validator = node.tree.validator;
3650
+
3651
+ if (Lang.isFunction(validator)) {
3652
+ value = validator(value,node.label,node);
3653
+ if (Lang.isUndefined(value)) { return false; }
3654
+ }
3655
+ node.label = value;
3656
+ node.getLabelEl().innerHTML = value;
3657
+ };
3658
+
3659
+ /**
3660
+ * Destroys the contents of the inline editor panel
3661
+ * Overrides Node.destroyEditorContent
3662
+ * Since we didn't set any event listeners on this inline editor, it is more efficient to avoid the generic method in Node
3663
+ * @method destroyEditorContents
3664
+ * @param editorData {YAHOO.widget.TreeView.editorData} a shortcut to the static object holding editing information
3665
+ * @for YAHOO.widget.TextNode
3666
+ */
3667
+ TNproto.destroyEditorContents = function (editorData) {
3668
+ editorData.inputContainer.innerHTML = '';
3669
+ };
3670
+ })();
3671
+ YAHOO.register("treeview", YAHOO.widget.TreeView, {version: "2.7.0", build: "1796"});