]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * This class manages bindings for a `Session` or `ViewModel`.\r | |
3 | * @private\r | |
4 | */\r | |
5 | Ext.define('Ext.app.bind.AbstractStub', {\r | |
6 | extend: 'Ext.util.Schedulable',\r | |
7 | \r | |
8 | requires: [\r | |
9 | 'Ext.app.bind.Binding'\r | |
10 | ],\r | |
11 | \r | |
12 | children: null,\r | |
13 | \r | |
14 | depth: 0,\r | |
15 | \r | |
16 | generation: 1,\r | |
17 | \r | |
18 | kind: 10,\r | |
19 | \r | |
20 | parent: null,\r | |
21 | \r | |
22 | constructor: function (owner, name) {\r | |
23 | var me = this;\r | |
24 | \r | |
25 | /**\r | |
26 | * @property {Ext.data.Session/Ext.app.ViewModel} owner\r | |
27 | * This property is set at creation of ths stub and should not be changed.\r | |
28 | * @readonly\r | |
29 | */\r | |
30 | me.owner = owner;\r | |
31 | me.name = name;\r | |
32 | \r | |
33 | me.callParent();\r | |
34 | },\r | |
35 | \r | |
36 | destroy: function () {\r | |
37 | var me = this,\r | |
38 | children = me.children,\r | |
39 | bindings = me.bindings,\r | |
40 | len, i, key;\r | |
41 | \r | |
42 | if (bindings) {\r | |
43 | for (i = 0, len = bindings.length; i < len; ++i) {\r | |
44 | bindings[i].destroy(true);\r | |
45 | }\r | |
46 | }\r | |
47 | \r | |
48 | for (key in children) {\r | |
49 | children[key].destroy();\r | |
50 | }\r | |
51 | me.callParent();\r | |
52 | me.bindings = me.children = me.owner = null;\r | |
53 | },\r | |
54 | \r | |
55 | add: function (child) {\r | |
56 | var me = this;\r | |
57 | \r | |
58 | (me.children || (me.children = {}))[child.name] = child;\r | |
59 | \r | |
60 | child.depth = me.depth + 1;\r | |
61 | child.parent = me;\r | |
62 | },\r | |
63 | \r | |
64 | getChild: function (path) {\r | |
65 | var pathArray = Ext.isString(path) ? path.split('.') : path;\r | |
66 | \r | |
67 | if (pathArray && pathArray.length) {\r | |
68 | return this.descend(pathArray, 0);\r | |
69 | }\r | |
70 | \r | |
71 | return this;\r | |
72 | },\r | |
73 | \r | |
74 | getFullName: function () {\r | |
75 | var me = this,\r | |
76 | name = me.fullName,\r | |
77 | parent = me.parent,\r | |
78 | s;\r | |
79 | \r | |
80 | if (!name) {\r | |
81 | name = me.name || me.id;\r | |
82 | if (parent && (s = parent.getFullName())) {\r | |
83 | name = ((s.charAt(s.length-1) !== ':') ? s + '.' : s) + name;\r | |
84 | }\r | |
85 | me.fullName = name;\r | |
86 | }\r | |
87 | \r | |
88 | return name;\r | |
89 | },\r | |
90 | \r | |
91 | getSession: function () {\r | |
92 | var owner = this.owner;\r | |
93 | \r | |
94 | return owner.isSession ? owner : owner.getSession();\r | |
95 | },\r | |
96 | \r | |
97 | bind: function (callback, scope, options) {\r | |
98 | var me = this,\r | |
99 | binding = new Ext.app.bind.Binding(me, callback, scope, options),\r | |
100 | bindings = (me.bindings || (me.bindings = []));\r | |
101 | \r | |
102 | binding.depth = me.depth;\r | |
103 | bindings.push(binding);\r | |
104 | \r | |
105 | return binding;\r | |
106 | },\r | |
107 | \r | |
108 | getValue: function () {\r | |
109 | return this.isLoading() ? null : this.getRawValue();\r | |
110 | },\r | |
111 | \r | |
112 | graft: function (replacement) {\r | |
113 | var me = this,\r | |
114 | bindings = me.bindings,\r | |
115 | name = me.name,\r | |
116 | i;\r | |
117 | \r | |
118 | // Clear these so that when we call destroy we won't damage anything:\r | |
119 | me.parent = me.bindings = null;\r | |
120 | me.destroy(); // we may be scheduled\r | |
121 | \r | |
122 | replacement.depth = me.depth;\r | |
123 | replacement.bindings = bindings;\r | |
124 | replacement.generation = me.generation + 1;\r | |
125 | replacement.name = name;\r | |
126 | replacement.id = me.id;\r | |
127 | replacement.path = me.path;\r | |
128 | \r | |
129 | // Now for the fun part...\r | |
130 | if (bindings) {\r | |
131 | for (i = bindings.length; i-- > 0; ) {\r | |
132 | bindings[i].stub = replacement;\r | |
133 | }\r | |
134 | } \r | |
135 | \r | |
136 | return replacement;\r | |
137 | },\r | |
138 | \r | |
139 | isDescendantOf: function (item) {\r | |
140 | for (var parent = this; parent = parent.parent; ) {\r | |
141 | if (parent === item) {\r | |
142 | return true;\r | |
143 | }\r | |
144 | }\r | |
145 | return false;\r | |
146 | },\r | |
147 | \r | |
148 | onSchedule: function() {\r | |
149 | // When a stub changes, say "foo.bar.baz" we may need to notify bindings on our\r | |
150 | // parents "foo.bar" and "foo", This is true especially when these are targets of\r | |
151 | // links. To economize on this we require that bindings that want to be notified\r | |
152 | // of changes to sub-properties of their target set the "deep" property to true.\r | |
153 | for (var i, len, binding, bindings, p = this.parent; p; p = p.parent) {\r | |
154 | bindings = p.bindings;\r | |
155 | if (bindings) {\r | |
156 | for (i = 0, len = bindings.length; i < len; ++i) {\r | |
157 | binding = bindings[i];\r | |
158 | if (binding.deep && !binding.scheduled) {\r | |
159 | binding.schedule();\r | |
160 | }\r | |
161 | }\r | |
162 | }\r | |
163 | }\r | |
164 | },\r | |
165 | \r | |
166 | react: function () {\r | |
167 | var bindings = this.bindings,\r | |
168 | binding, i, len;\r | |
169 | \r | |
170 | if (bindings) {\r | |
171 | for (i = 0, len = bindings.length; i < len; ++i) {\r | |
172 | binding = bindings[i];\r | |
173 | if (!binding.scheduled) {\r | |
174 | binding.schedule();\r | |
175 | }\r | |
176 | }\r | |
177 | }\r | |
178 | },\r | |
179 | \r | |
180 | unbind: function (binding) {\r | |
181 | var bindings = this.bindings;\r | |
182 | \r | |
183 | if (bindings && bindings.length) {\r | |
184 | Ext.Array.remove(bindings, binding);\r | |
185 | }\r | |
186 | },\r | |
187 | \r | |
188 | privates: {\r | |
189 | collect: function() {\r | |
190 | var children = this.children,\r | |
191 | bindings = this.bindings,\r | |
192 | totalCount = 0,\r | |
193 | count = 0,\r | |
194 | child,\r | |
195 | key;\r | |
196 | \r | |
197 | if (children) {\r | |
198 | for (key in children) {\r | |
199 | child = children[key];\r | |
200 | count = child.collect();\r | |
201 | if (count === 0) {\r | |
202 | // The child (and any deep children) have no bindings,\r | |
203 | // so we can consider it a dead node.\r | |
204 | child.destroy();\r | |
205 | delete children[key];\r | |
206 | }\r | |
207 | totalCount += count;\r | |
208 | }\r | |
209 | }\r | |
210 | \r | |
211 | if (bindings) {\r | |
212 | totalCount += bindings.length;\r | |
213 | }\r | |
214 | \r | |
215 | return totalCount;\r | |
216 | },\r | |
217 | \r | |
218 | getScheduler: function () {\r | |
219 | var owner = this.owner;\r | |
220 | return owner && owner.getScheduler();\r | |
221 | },\r | |
222 | \r | |
223 | sort: function () {\r | |
224 | var parent = this.parent;\r | |
225 | \r | |
226 | if (parent) {\r | |
227 | // We sort our parent first because if it is something like a link we need\r | |
228 | // it to determine the value of the root-level property before we can dot\r | |
229 | // our way into it. This is especially important for formulas that might\r | |
230 | // throw errors if the links have not published results before they run.\r | |
231 | this.scheduler.sortItem(parent);\r | |
232 | }\r | |
233 | \r | |
234 | // Schedulable#sort === emptyFn\r | |
235 | //me.callParent();\r | |
236 | }\r | |
237 | }\r | |
238 | });\r |