Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/datasource/jsdatasource.js
2868 views
1
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview An implementation of DataNode for wrapping JS data.
17
*
18
*/
19
20
21
goog.provide('goog.ds.JsDataSource');
22
goog.provide('goog.ds.JsPropertyDataSource');
23
24
goog.require('goog.ds.BaseDataNode');
25
goog.require('goog.ds.BasicNodeList');
26
goog.require('goog.ds.DataManager');
27
goog.require('goog.ds.DataNode');
28
goog.require('goog.ds.EmptyNodeList');
29
goog.require('goog.ds.LoadState');
30
31
32
/**
33
* Data source whose backing is JavaScript data
34
*
35
* Names that are reserved for system use and shouldn't be used for data node
36
* names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is
37
* undefined if these names are used.
38
*
39
* @param {Object} root The root JS node.
40
* @param {string} dataName The name of this node relative to the parent node.
41
* @param {Object=} opt_parent Optional parent of this JsDataSource.
42
*
43
* implements goog.ds.DataNode.
44
* @constructor
45
* @extends {goog.ds.DataNode}
46
*/
47
// TODO(arv): Use interfaces when available.
48
goog.ds.JsDataSource = function(root, dataName, opt_parent) {
49
this.parent_ = opt_parent;
50
this.dataName_ = dataName;
51
this.setRoot(root);
52
};
53
54
55
/**
56
* The root JS object. Can be null.
57
* @type {*}
58
* @protected
59
* @suppress {underscore|visibility}
60
*/
61
goog.ds.JsDataSource.prototype.root_;
62
63
64
/**
65
* Sets the root JS object
66
* @param {Object} root The root JS object. Can be null.
67
*
68
* @protected
69
*/
70
goog.ds.JsDataSource.prototype.setRoot = function(root) {
71
this.root_ = root;
72
this.childNodeList_ = null;
73
};
74
75
76
/**
77
* Set this data source to use list semantics. List data sources:
78
* - Are assumed to have child nodes of all of the same type of data
79
* - Fire data changes on the root node of the list whenever children
80
* are added or removed
81
* @param {?boolean} isList True to use list semantics.
82
* @private
83
*/
84
goog.ds.JsDataSource.prototype.setIsList_ = function(isList) {
85
this.isList_ = isList;
86
};
87
88
89
/** @override */
90
goog.ds.JsDataSource.prototype.get = function() {
91
return !goog.isObject(this.root_) ? this.root_ : this.getChildNodes();
92
};
93
94
95
/**
96
* Set the value of the node
97
* @param {*} value The new value of the node.
98
* @override
99
*/
100
goog.ds.JsDataSource.prototype.set = function(value) {
101
if (value && goog.isObject(this.root_)) {
102
throw Error('Can\'t set group nodes to new values yet');
103
}
104
105
if (this.parent_) {
106
this.parent_.root_[this.dataName_] = value;
107
}
108
this.root_ = value;
109
this.childNodeList_ = null;
110
111
goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath());
112
};
113
114
115
/**
116
* TODO(user) revisit lazy creation.
117
* @override
118
*/
119
goog.ds.JsDataSource.prototype.getChildNodes = function(opt_selector) {
120
if (!this.root_) {
121
return new goog.ds.EmptyNodeList();
122
}
123
124
if (!opt_selector || opt_selector == goog.ds.STR_ALL_CHILDREN_SELECTOR) {
125
this.createChildNodes_(false);
126
return this.childNodeList_;
127
} else if (opt_selector.indexOf(goog.ds.STR_WILDCARD) == -1) {
128
if (this.root_[opt_selector] != null) {
129
return new goog.ds.BasicNodeList([this.getChildNode(opt_selector)]);
130
} else {
131
return new goog.ds.EmptyNodeList();
132
}
133
} else {
134
throw Error('Selector not supported yet (' + opt_selector + ')');
135
}
136
137
};
138
139
140
/**
141
* Creates the DataNodeList with the child nodes for this element.
142
* Allows for only building list as needed.
143
*
144
* @param {boolean=} opt_force Whether to force recreating child nodes,
145
* defaults to false.
146
* @private
147
*/
148
goog.ds.JsDataSource.prototype.createChildNodes_ = function(opt_force) {
149
if (this.childNodeList_ && !opt_force) {
150
return;
151
}
152
153
if (!goog.isObject(this.root_)) {
154
this.childNodeList_ = new goog.ds.EmptyNodeList();
155
return;
156
}
157
158
var childNodeList = new goog.ds.BasicNodeList();
159
var newNode;
160
if (goog.isArray(this.root_)) {
161
var len = this.root_.length;
162
for (var i = 0; i < len; i++) {
163
// "id" is reserved node name that will map to a named child node
164
// TODO(user) Configurable logic for choosing id node
165
var node = this.root_[i];
166
var id = node.id;
167
var name = id != null ? String(id) : '[' + i + ']';
168
newNode = new goog.ds.JsDataSource(node, name, this);
169
childNodeList.add(newNode);
170
}
171
} else {
172
for (var name in this.root_) {
173
var obj = this.root_[name];
174
// If the node is already a datasource, then add it.
175
if (obj.getDataName) {
176
childNodeList.add(obj);
177
} else if (!goog.isFunction(obj)) {
178
newNode = new goog.ds.JsDataSource(obj, name, this);
179
childNodeList.add(newNode);
180
}
181
}
182
}
183
this.childNodeList_ = childNodeList;
184
};
185
186
187
/**
188
* Gets a named child node of the current node
189
* @param {string} name The node name.
190
* @param {boolean=} opt_canCreate If true, can create child node.
191
* @return {goog.ds.DataNode} The child node, or null if no node of
192
* this name exists.
193
* @override
194
*/
195
goog.ds.JsDataSource.prototype.getChildNode = function(name, opt_canCreate) {
196
if (!this.root_) {
197
return null;
198
}
199
var node = /** @type {goog.ds.DataNode} */ (this.getChildNodes().get(name));
200
if (!node && opt_canCreate) {
201
var newObj = {};
202
if (goog.isArray(this.root_)) {
203
newObj['id'] = name;
204
this.root_.push(newObj);
205
} else {
206
this.root_[name] = newObj;
207
}
208
node = new goog.ds.JsDataSource(newObj, name, this);
209
if (this.childNodeList_) {
210
this.childNodeList_.add(node);
211
}
212
}
213
return node;
214
};
215
216
217
/**
218
* Gets the value of a child node
219
* @param {string} name The node name.
220
* @return {Object} The value of the node, or null if no value or the child
221
* node doesn't exist.
222
* @override
223
*/
224
goog.ds.JsDataSource.prototype.getChildNodeValue = function(name) {
225
if (this.childNodeList_) {
226
var node = this.getChildNodes().get(name);
227
return node ? node.get() : null;
228
} else if (this.root_) {
229
return this.root_[name];
230
} else {
231
return null;
232
}
233
};
234
235
236
/**
237
* Sets a named child node of the current node.
238
* If value is null, removes the child node.
239
* @param {string} name The node name.
240
* @param {Object} value The value to set, can be DataNode, object,
241
* property, or null.
242
* @return {Object} The child node, if set.
243
* @override
244
*/
245
goog.ds.JsDataSource.prototype.setChildNode = function(name, value) {
246
var removedPath = null;
247
var node = null;
248
var addedNode = false;
249
250
// Set node to the DataNode to add - if the value isn't already a DataNode,
251
// creates a JsDataSource or JsPropertyDataSource wrapper
252
if (value != null) {
253
if (value.getDataName) {
254
// The value is a DataNode. We must update its parent.
255
node = value;
256
node.parent_ = this;
257
} else {
258
if (goog.isArray(value) || goog.isObject(value)) {
259
node = new goog.ds.JsDataSource(value, name, this);
260
} else {
261
node = new goog.ds.JsPropertyDataSource(
262
/** @type {goog.ds.DataNode} */ (this.root_), name, this);
263
}
264
}
265
}
266
267
// This logic will get cleaner once we can remove the backing array / object
268
// and just rely on the childNodeList_. This is needed until dependent code
269
// is cleaned up.
270
// TODO(user) Remove backing array / object and just use childNodeList_
271
272
if (goog.isArray(this.root_)) {
273
// To remove by name, need to create a map of the child nodes by ID
274
this.createChildNodes_();
275
var index = this.childNodeList_.indexOf(name);
276
if (value == null) {
277
// Remove the node
278
var nodeToRemove = this.childNodeList_.get(name);
279
if (nodeToRemove) {
280
removedPath = nodeToRemove.getDataPath();
281
}
282
this.root_.splice(index, 1);
283
} else {
284
// Add the node
285
if (index) {
286
this.root_[index] = value;
287
} else {
288
this.root_.push(value);
289
}
290
}
291
if (index == null) {
292
addedNode = true;
293
}
294
this.childNodeList_.setNode(name, /** @type {goog.ds.DataNode} */ (node));
295
} else if (goog.isObject(this.root_)) {
296
if (value == null) {
297
// Remove the node
298
this.createChildNodes_();
299
var nodeToRemove = this.childNodeList_.get(name);
300
if (nodeToRemove) {
301
removedPath = nodeToRemove.getDataPath();
302
}
303
delete this.root_[name];
304
} else {
305
// Add the node
306
if (!this.root_[name]) {
307
addedNode = true;
308
}
309
this.root_[name] = value;
310
}
311
// Only need to update childNodeList_ if has been created already
312
if (this.childNodeList_) {
313
this.childNodeList_.setNode(name, /** @type {goog.ds.DataNode} */ (node));
314
}
315
}
316
317
// Fire the event that the node changed
318
var dm = goog.ds.DataManager.getInstance();
319
if (node) {
320
dm.fireDataChange(node.getDataPath());
321
if (addedNode && this.isList()) {
322
dm.fireDataChange(this.getDataPath());
323
dm.fireDataChange(this.getDataPath() + '/count()');
324
}
325
} else if (removedPath) {
326
dm.fireDataChange(removedPath);
327
if (this.isList()) {
328
dm.fireDataChange(this.getDataPath());
329
dm.fireDataChange(this.getDataPath() + '/count()');
330
}
331
}
332
return node;
333
};
334
335
336
/**
337
* Get the name of the node relative to the parent node
338
* @return {string} The name of the node.
339
* @override
340
*/
341
goog.ds.JsDataSource.prototype.getDataName = function() {
342
return this.dataName_;
343
};
344
345
346
/**
347
* Setthe name of the node relative to the parent node
348
* @param {string} dataName The name of the node.
349
* @override
350
*/
351
goog.ds.JsDataSource.prototype.setDataName = function(dataName) {
352
this.dataName_ = dataName;
353
};
354
355
356
/**
357
* Gets the a qualified data path to this node
358
* @return {string} The data path.
359
* @override
360
*/
361
goog.ds.JsDataSource.prototype.getDataPath = function() {
362
var parentPath = '';
363
if (this.parent_) {
364
parentPath = this.parent_.getDataPath() + goog.ds.STR_PATH_SEPARATOR;
365
}
366
367
return parentPath + this.dataName_;
368
};
369
370
371
/**
372
* Load or reload the backing data for this node
373
* @override
374
*/
375
goog.ds.JsDataSource.prototype.load = function() {
376
// Nothing to do
377
};
378
379
380
/**
381
* Gets the state of the backing data for this node
382
* TODO(user) Discuss null value handling
383
* @return {goog.ds.LoadState} The state.
384
* @override
385
*/
386
goog.ds.JsDataSource.prototype.getLoadState = function() {
387
return (this.root_ == null) ? goog.ds.LoadState.NOT_LOADED :
388
goog.ds.LoadState.LOADED;
389
};
390
391
392
/**
393
* Whether the value of this node is a homogeneous list of data
394
* @return {boolean} True if a list.
395
* @override
396
*/
397
goog.ds.JsDataSource.prototype.isList = function() {
398
return this.isList_ != null ? this.isList_ : goog.isArray(this.root_);
399
};
400
401
402
403
/**
404
* Data source for JavaScript properties that arent objects. Contains reference
405
* to parent object so that you can set the vaule
406
*
407
* @param {goog.ds.DataNode} parent Parent object.
408
* @param {string} dataName Name of this property.
409
* @param {goog.ds.DataNode=} opt_parentDataNode The parent data node. If
410
* omitted, assumes that the parent object is the parent data node.
411
*
412
* @constructor
413
* @extends {goog.ds.BaseDataNode}
414
* @final
415
*/
416
goog.ds.JsPropertyDataSource = function(parent, dataName, opt_parentDataNode) {
417
goog.ds.BaseDataNode.call(this);
418
this.dataName_ = dataName;
419
this.parent_ = parent;
420
this.parentDataNode_ = opt_parentDataNode || this.parent_;
421
};
422
goog.inherits(goog.ds.JsPropertyDataSource, goog.ds.BaseDataNode);
423
424
425
/**
426
* Get the value of the node
427
* @return {Object} The value of the node, or null if no value.
428
*/
429
goog.ds.JsPropertyDataSource.prototype.get = function() {
430
return this.parent_[this.dataName_];
431
};
432
433
434
/**
435
* Set the value of the node
436
* @param {Object} value The new value of the node.
437
* @override
438
*/
439
goog.ds.JsPropertyDataSource.prototype.set = function(value) {
440
var oldValue = this.parent_[this.dataName_];
441
this.parent_[this.dataName_] = value;
442
443
if (oldValue != value) {
444
goog.ds.DataManager.getInstance().fireDataChange(this.getDataPath());
445
}
446
};
447
448
449
/**
450
* Get the name of the node relative to the parent node
451
* @return {string} The name of the node.
452
* @override
453
*/
454
goog.ds.JsPropertyDataSource.prototype.getDataName = function() {
455
return this.dataName_;
456
};
457
458
459
/** @override */
460
goog.ds.JsPropertyDataSource.prototype.getParent = function() {
461
return this.parentDataNode_;
462
};
463
464