1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
base.exportTo('ui', function() {
/**
* Decorates elements as an instance of a class.
* @param {string|!Element} source The way to find the element(s) to decorate.
* If this is a string then {@code querySeletorAll} is used to find the
* elements to decorate.
* @param {!Function} constr The constructor to decorate with. The constr
* needs to have a {@code decorate} function.
*/
function decorate(source, constr) {
var elements;
if (typeof source == 'string')
elements = base.doc.querySelectorAll(source);
else
elements = [source];
for (var i = 0, el; el = elements[i]; i++) {
if (!(el instanceof constr))
constr.decorate(el);
}
}
/**
* Defines a tracing UI component, a function that can be called to construct
* the component.
*
* Base class:
* <pre>
* var List = ui.define('list');
* List.prototype = {
* __proto__: HTMLUListElement.prototype,
* decorate: function() {
* ...
* },
* ...
* };
* </pre>
*
* Derived class:
* <pre>
* var CustomList = ui.define('custom-list', List);
* CustomList.prototype = {
* __proto__: List.prototype,
* decorate: function() {
* ...
* },
* ...
* };
* </pre>
*
* @param {string} tagName The tagName of the newly created subtype. If
* subclassing, this is used for debugging. If not subclassing, then it is
* the tag name that will be created by the component.
* @param {function=} opt_parentConstructor The parent class for this new
* element, if subclassing is desired. If provided, the parent class must
* be also a function created by ui.define.
* @return {function(Object=):Element} The newly created component
* constructor.
*/
function define(tagName, opt_parentConstructor) {
if (typeof tagName == 'function') {
throw new Error('Passing functions as tagName is deprecated. Please ' +
'use (tagName, opt_parentConstructor) to subclass');
}
var tagName = tagName.toLowerCase();
if (opt_parentConstructor && !opt_parentConstructor.tagName)
throw new Error('opt_parentConstructor was not created by ui.define');
/**
* Creates a new UI element constructor.
* Arguments passed to the constuctor are provided to the decorate method.
* You will need to call the parent elements decorate method from within
* your decorate method and pass any required parameters.
* @constructor
*/
function f() {
if (opt_parentConstructor &&
f.prototype.__proto__ != opt_parentConstructor.prototype) {
throw new Error(
tagName + ' prototye\'s __proto__ field is messed up. ' +
'It MUST be the prototype of ' + opt_parentConstructor.tagName);
}
// Walk up the parent constructors until we can find the type of tag
// to create.
var tag = tagName;
if (opt_parentConstructor) {
var parent = opt_parentConstructor;
while (parent && parent.tagName) {
tag = parent.tagName;
parent = parent.parentConstructor;
}
}
var el = base.doc.createElement(tag);
f.decorate.call(this, el, arguments);
return el;
}
try {
// f.name is not directly writable. So make it writable anyway.
Object.defineProperty(
f, 'name',
{value: tagName, writable: false, configurable: false});
} catch (e) {
// defineProperty throws a TypeError about name already being defined
// although, it also correctly sets the value to tagName.
}
/**
* Decorates an element as a UI element class.
* @param {!Element} el The element to decorate.
*/
f.decorate = function(el) {
el.__proto__ = f.prototype;
el.decorate.apply(el, arguments[1]);
el.constructor = f;
};
f.tagName = tagName;
f.parentConstructor = (opt_parentConstructor ? opt_parentConstructor :
undefined);
f.toString = function() {
if (!f.parentConstructor)
return f.tagName;
return f.parentConstructor.toString() + '::' + f.tagName;
};
return f;
}
function elementIsChildOf(el, potentialParent) {
if (el == potentialParent)
return false;
var cur = el;
while (cur.parentNode) {
if (cur == potentialParent)
return true;
cur = cur.parentNode;
}
return false;
};
return {
decorate: decorate,
define: define,
elementIsChildOf: elementIsChildOf
};
});
|