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.
- data/ChangeLog +24 -1
- data/README +0 -0
- data/base/shared_lib/moving_average.rb +34 -33
- data/base/shared_lib/system/cross.rb +76 -0
- data/base/shared_lib/system/position_manager.rb +160 -0
- data/base/shared_lib/system/signal.rb +347 -0
- data/html/css/datatable.css +4 -4
- data/html/css/default.css +57 -5
- data/html/css/treeview.css +205 -0
- data/html/img/bin_closed.png +0 -0
- data/html/img/bin_empty.png +0 -0
- data/html/img/button_add_small.gif +0 -0
- data/html/img/button_add_small_gray.gif +0 -0
- data/html/img/button_add_small_over.gif +0 -0
- data/html/img/button_mkcol.gif +0 -0
- data/html/img/button_mkcol_gray.gif +0 -0
- data/html/img/button_mkcol_over.gif +0 -0
- data/html/img/button_remove_small.gif +0 -0
- data/html/img/button_remove_small_gray.gif +0 -0
- data/html/img/button_remove_small_over.gif +0 -0
- data/html/img/button_rename.gif +0 -0
- data/html/img/button_rename_gray.gif +0 -0
- data/html/img/button_rename_over.gif +0 -0
- data/html/img/control_play.png +0 -0
- data/html/img/control_play_blue.png +0 -0
- data/html/img/folder.png +0 -0
- data/html/img/folder_brick.png +0 -0
- data/html/img/folder_user.png +0 -0
- data/html/{js/codepress/images → img}/line-numbers.png +0 -0
- data/html/img/page_white_ruby.png +0 -0
- data/html/img/sidebar_agent_edit.png +0 -0
- data/html/img/sidebar_agent_edit_over.png +0 -0
- data/html/img/sidebar_agent_edit_s.png +0 -0
- data/html/img/yui/treeview-sprite.gif +0 -0
- data/html/index.html +34 -18
- data/html/js/agent-editor/agent-editor-page.js +324 -0
- data/html/js/agent-editor/agent-editor.js +363 -0
- data/html/js/agent-editor/agent-list-tree.js +251 -0
- data/html/js/agent-selector.js +23 -28
- data/html/js/app.js +63 -91
- data/html/js/bt-create-page.js +25 -19
- data/html/js/container-min.js +1 -1
- data/html/js/container.js +944 -0
- data/html/js/edit_area/autocompletion.js +11 -13
- data/html/js/edit_area/edit_area.css +79 -40
- data/html/js/edit_area/edit_area.js +255 -226
- data/html/js/edit_area/edit_area_compressor.php +4 -4
- data/html/js/edit_area/edit_area_full.gz +0 -0
- data/html/js/edit_area/edit_area_full.js +31 -31
- data/html/js/edit_area/edit_area_full_with_plugins.gz +0 -0
- data/html/js/edit_area/edit_area_full_with_plugins.js +31 -31
- data/html/js/edit_area/edit_area_functions.js +448 -341
- data/html/js/edit_area/edit_area_loader.js +409 -397
- data/html/js/edit_area/elements_functions.js +120 -123
- data/html/js/edit_area/highlight.js +305 -197
- data/html/js/edit_area/images/goto.png +0 -0
- data/html/js/edit_area/images/help.png +0 -0
- data/html/js/edit_area/images/redo.png +0 -0
- data/html/js/edit_area/images/save.png +0 -0
- data/html/js/edit_area/images/search.png +0 -0
- data/html/js/edit_area/images/undo.png +0 -0
- data/html/js/edit_area/images/word_wrap.gif +0 -0
- data/html/js/edit_area/keyboard.js +5 -5
- data/html/js/edit_area/langs/bg.js +73 -0
- data/html/js/edit_area/langs/cs.js +2 -0
- data/html/js/edit_area/langs/de.js +2 -0
- data/html/js/edit_area/langs/dk.js +2 -0
- data/html/js/edit_area/langs/en.js +2 -0
- data/html/js/edit_area/langs/eo.js +2 -0
- data/html/js/edit_area/langs/es.js +2 -0
- data/html/js/edit_area/langs/fi.js +67 -0
- data/html/js/edit_area/langs/fr.js +2 -0
- data/html/js/edit_area/langs/hr.js +2 -0
- data/html/js/edit_area/langs/it.js +2 -0
- data/html/js/edit_area/langs/ja.js +2 -0
- data/html/js/edit_area/langs/mk.js +2 -0
- data/html/js/edit_area/langs/nl.js +2 -0
- data/html/js/edit_area/langs/pl.js +2 -0
- data/html/js/edit_area/langs/pt.js +2 -0
- data/html/js/edit_area/langs/ru.js +2 -0
- data/html/js/edit_area/langs/sk.js +2 -0
- data/html/js/edit_area/langs/zh.js +67 -0
- data/html/js/edit_area/manage_area.js +362 -205
- data/html/js/edit_area/plugins/charmap/langs/bg.js +12 -0
- data/html/js/edit_area/plugins/charmap/langs/zh.js +6 -0
- data/html/js/edit_area/plugins/test/langs/bg.js +10 -0
- data/html/js/edit_area/plugins/test/langs/zh.js +4 -0
- data/html/js/edit_area/reg_syntax/java.js +56 -0
- data/html/js/edit_area/reg_syntax/ruby.js +9 -9
- data/html/js/edit_area/reg_syntax.js +15 -13
- data/html/js/edit_area/regexp.js +36 -32
- data/html/js/edit_area/resize_area.js +43 -47
- data/html/js/edit_area/search_replace.js +29 -29
- data/html/js/edit_area/template.html +6 -4
- data/html/js/json-broker-client.js +23 -17
- data/html/js/result-page.js +107 -57
- data/html/js/rt-setting-page.js +38 -15
- data/html/js/sidebar.js +41 -27
- data/html/js/templates.js +167 -32
- data/html/js/utils.js +143 -7
- data/html/js/yui/treeview.js +3671 -0
- data/html/swf/chart.swf +0 -0
- data/html/test/agent_editor_spec.js +815 -0
- data/html/test/index.html +40 -0
- data/html/test/jsspec/JSSpec.css +224 -0
- data/html/test/jsspec/JSSpec.js +1549 -0
- data/html/test/jsspec/diff_match_patch.js +1 -0
- data/html/test/utils_spec.js +111 -0
- data/lib/jiji/agent/agent.rb +69 -12
- data/lib/jiji/agent/agent_manager.rb +18 -12
- data/lib/jiji/agent/agent_registry.rb +35 -121
- data/lib/jiji/collector.rb +16 -6
- data/lib/jiji/command.rb +46 -5
- data/lib/jiji/dao/file_system_dao.rb +158 -0
- data/lib/jiji/dao/timed_data_dao.rb +2 -0
- data/lib/jiji/dao/trade_result_dao.rb +1 -1
- data/lib/jiji/error.rb +24 -8
- data/lib/jiji/migration/migrator1_2_0.rb +67 -0
- data/lib/jiji/models.rb +82 -24
- data/lib/jiji/operator.rb +55 -51
- data/lib/jiji/output.rb +85 -29
- data/lib/jiji/output_manager.rb +84 -0
- data/lib/jiji/plugin/embedded/single_click_client.rb +2 -2
- data/lib/jiji/plugin/securities_plugin.rb +0 -1
- data/lib/jiji/process.rb +229 -208
- data/lib/jiji/process_manager.rb +190 -96
- data/lib/jiji/registry.rb +87 -19
- data/lib/jiji/server.rb +1 -0
- data/lib/jiji/service/agent_service.rb +147 -48
- data/lib/jiji/service/output_service.rb +37 -17
- data/lib/jiji/service/process_service.rb +3 -5
- data/lib/jiji/service/trade_result_service.rb +4 -5
- data/lib/jiji/util/file_lock.rb +4 -4
- data/lib/jiji/util/include_proxy.rb +17 -0
- data/lib/jiji/util/json_broker.rb +6 -4
- data/lib/jiji/util/util.rb +1 -1
- data/swf/chart/fx/chart/Chart.as +7 -0
- data/swf/chart/fx/chart/ui/graph/GraphManager.as +15 -2
- data/test/ProcessTest/agents/foo.rb +10 -0
- data/test/ProcessTest/conf/configuration.yaml +3 -0
- data/test/agent/agent_tests.rb +10 -0
- data/test/agent/test_AgentManager.rb +28 -12
- data/test/agent/test_AgentRegistry.rb +194 -99
- data/test/agent/test_PeriodicallyAgent.rb +1 -2
- data/test/agent/test_Permitter.rb +1 -2
- data/test/all_tests.rb +7 -19
- data/test/dao/dao_tests.rb +9 -0
- data/test/dao/test_FileSystemDao.rb +431 -0
- data/test/dao/test_RateDao.rb +5 -7
- data/test/dao/test_TradeResultDao.rb +1 -2
- data/test/migration/migration_tests.rb +10 -0
- data/test/migration/migrator1_2_0test_data/basic/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/basic/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/basic/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/basic/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
- data/test/migration/migrator1_2_0test_data/basic/props.yaml +85 -0
- data/test/migration/migrator1_2_0test_data/illegal_props/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/illegal_props/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/illegal_props/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/illegal_props/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
- data/test/migration/migrator1_2_0test_data/illegal_props/props.yaml +1 -0
- data/test/migration/migrator1_2_0test_data/no_outs/props.yaml +85 -0
- data/test/migration/migrator1_2_0test_data/no_props/out/M2NlOTA2ODEtZDdlNi00NWU1LWIwNDQtMjBmODY2ZGNkNzBj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/no_props/out/MDVhYzcxMjYtMGFlMS00Mzk0LWEyNmUtYjVjZjgwNDA0ZmE2/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/no_props/out/MzA1YTk0NDgtNzhjNi00NDk3LTk2NTktYzE1ZjBhNzdiYjNj/56e75YuV5bmz5Z2H57ea/meta.yaml +8 -0
- data/test/migration/migrator1_2_0test_data/no_props/out/YjRkOTI1MzEtZTM4MS00YjQwLTg1ZTQtMWFmZDRlNDUwMzBm/56e75YuV5bmz5Z2H57ea/meta.yaml +9 -0
- data/test/migration/test_Migrator.rb +1 -1
- data/test/migration/test_Migrator1_0_3.rb +1 -1
- data/test/migration/test_Migrator1_1_0.rb +1 -1
- data/test/migration/test_Migrator1_2_0.rb +94 -0
- data/test/plugin/embedded/test_SingleClickClient.rb +1 -2
- data/test/plugin/plugin_tests.rb +8 -0
- data/test/plugin/test_Loader.rb +1 -1
- data/test/shared/rate.csv +144 -0
- data/test/shared/shared_tests.rb +9 -0
- data/test/shared/test_Cross.rb +144 -0
- data/test/shared/test_PositionManager.rb +285 -0
- data/test/shared/test_Signal.rb +65 -0
- data/test/test_Output.rb +28 -21
- data/test/test_OutputManager.rb +162 -0
- data/test/test_Output_registry.rb +6 -17
- data/test/test_Process.rb +434 -222
- data/test/test_ProcessManager.rb +458 -101
- data/test/test_utils.rb +71 -8
- data/test/util/test_BlockToSession.rb +1 -2
- data/test/util/test_CSV.rb +1 -2
- data/test/util/test_SynchronizeInterceptor.rb +1 -2
- data/test/util/util_tests.rb +9 -0
- metadata +127 -47
- data/html/js/agent-editor-page.js +0 -440
- data/html/js/codepress/codepress.css +0 -21
- data/html/js/codepress/codepress.html +0 -35
- data/html/js/codepress/codepress.js +0 -138
- data/html/js/codepress/engines/gecko.js +0 -293
- data/html/js/codepress/engines/khtml.js +0 -0
- data/html/js/codepress/engines/msie.js +0 -304
- data/html/js/codepress/engines/older.js +0 -0
- data/html/js/codepress/engines/opera.js +0 -260
- data/html/js/codepress/images/line-numbers.gif +0 -0
- data/html/js/codepress/index.html +0 -443
- data/html/js/codepress/languages/asp.css +0 -71
- data/html/js/codepress/languages/asp.js +0 -117
- data/html/js/codepress/languages/autoit.css +0 -13
- data/html/js/codepress/languages/autoit.js +0 -32
- data/html/js/codepress/languages/csharp.css +0 -9
- data/html/js/codepress/languages/csharp.js +0 -25
- data/html/js/codepress/languages/css.css +0 -10
- data/html/js/codepress/languages/css.js +0 -23
- data/html/js/codepress/languages/generic.css +0 -9
- data/html/js/codepress/languages/generic.js +0 -25
- data/html/js/codepress/languages/html.css +0 -13
- data/html/js/codepress/languages/html.js +0 -59
- data/html/js/codepress/languages/java.css +0 -7
- data/html/js/codepress/languages/java.js +0 -24
- data/html/js/codepress/languages/javascript.css +0 -8
- data/html/js/codepress/languages/javascript.js +0 -30
- data/html/js/codepress/languages/perl.css +0 -11
- data/html/js/codepress/languages/perl.js +0 -27
- data/html/js/codepress/languages/php.css +0 -12
- data/html/js/codepress/languages/php.js +0 -61
- data/html/js/codepress/languages/ruby.css +0 -10
- data/html/js/codepress/languages/ruby.js +0 -26
- data/html/js/codepress/languages/sql.css +0 -10
- data/html/js/codepress/languages/sql.js +0 -30
- data/html/js/codepress/languages/text.css +0 -5
- data/html/js/codepress/languages/text.js +0 -9
- data/html/js/codepress/languages/vbscript.css +0 -71
- data/html/js/codepress/languages/vbscript.js +0 -117
- data/html/js/codepress/languages/xsl.css +0 -15
- data/html/js/codepress/languages/xsl.js +0 -103
- data/html/js/codepress/license.txt +0 -458
- data/html/js/edit_area/images/Thumbs.db +0 -0
- 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 <UL> or <OL> elements containing <LI> elements.
|
481
|
+
* Each <LI> can have one element used as label and a second optional element which is to be a <UL> or <OL>
|
482
|
+
* containing nested nodes.
|
483
|
+
* Depending on what the first element of the <LI> element is, the following Nodes will be created: <ul>
|
484
|
+
* <li>plain text: a regular TextNode</li>
|
485
|
+
* <li>anchor <A>: 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 <LI> tag has a className called 'expanded'.
|
489
|
+
* All other className attributes will be copied over to the Node className property.
|
490
|
+
* If the <LI> 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] + '"> </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"> </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 <input> 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 (<div>)} element holding the in-line editor</li>
|
3375
|
+
* <li>inputContainer {HTMLelement (<div>)} element which will hold the type-specific input element(s) to be filled by the fillEditorContainer method</li>
|
3376
|
+
* <li>buttonsContainer {HTMLelement (<div>)} element which holds the <button> 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 <input> 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"});
|