1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 tvcm.exportTo('tvcm.ui', function() {
10 * Decorates elements as an instance of a class.
11 * @param {string|!Element} source The way to find the element(s) to decorate.
12 * If this is a string then {@code querySeletorAll} is used to find the
13 * elements to decorate.
14 * @param {!Function} constr The constructor to decorate with. The constr
15 * needs to have a {@code decorate} function.
17 function decorate(source, constr) {
19 if (typeof source == 'string')
20 elements = tvcm.doc.querySelectorAll(source);
24 for (var i = 0, el; el = elements[i]; i++) {
25 if (!(el instanceof constr))
31 * Defines a tracing UI component, a function that can be called to construct
36 * var List = tvcm.ui.define('list');
38 * __proto__: HTMLUListElement.prototype,
39 * decorate: function() {
48 * var CustomList = tvcm.ui.define('custom-list', List);
49 * CustomList.prototype = {
50 * __proto__: List.prototype,
51 * decorate: function() {
58 * @param {string} className The className of the newly created subtype. If
59 * subclassing by passing in opt_parentConstructor, this is used for
60 * debugging. If not subclassing, then it is the tag name that will be
61 * created by the component.
63 * @param {function=} opt_parentConstructor The parent class for this new
64 * element, if subclassing is desired. If provided, the parent class must
65 * be also a function created by tvcm.ui.define.
67 * @param {string=} opt_tagNS The namespace in which to create the base
68 * element. Has no meaning when opt_parentConstructor is passed and must
69 * either be undefined or the same namespace as the parent class.
71 * @return {function(Object=):Element} The newly created component
74 function define(className, opt_parentConstructor, opt_tagNS) {
75 if (typeof className == 'function') {
76 throw new Error('Passing functions as className is deprecated. Please ' +
77 'use (className, opt_parentConstructor) to subclass');
80 var className = className.toLowerCase();
81 if (opt_parentConstructor && !opt_parentConstructor.tagName)
82 throw new Error('opt_parentConstructor was not ' +
83 'created by tvcm.ui.define');
85 // Walk up the parent constructors until we can find the type of tag
87 var tagName = className;
88 var tagNS = undefined;
89 if (opt_parentConstructor) {
91 throw new Error('Must not specify tagNS if parentConstructor is given');
92 var parent = opt_parentConstructor;
93 while (parent && parent.tagName) {
94 tagName = parent.tagName;
96 parent = parent.parentConstructor;
103 * Creates a new UI element constructor.
104 * Arguments passed to the constuctor are provided to the decorate method.
105 * You will need to call the parent elements decorate method from within
106 * your decorate method and pass any required parameters.
110 if (opt_parentConstructor &&
111 f.prototype.__proto__ != opt_parentConstructor.prototype) {
113 className + ' prototye\'s __proto__ field is messed up. ' +
114 'It MUST be the prototype of ' + opt_parentConstructor.tagName);
118 if (tagNS === undefined)
119 el = tvcm.doc.createElement(tagName);
121 el = tvcm.doc.createElementNS(tagNS, tagName);
122 f.decorate.call(this, el, arguments);
127 * Decorates an element as a UI element class.
128 * @param {!Element} el The element to decorate.
130 f.decorate = function(el) {
131 el.__proto__ = f.prototype;
132 el.decorate.apply(el, arguments[1]);
136 f.className = className;
139 f.parentConstructor = (opt_parentConstructor ? opt_parentConstructor :
141 f.toString = function() {
142 if (!f.parentConstructor)
144 return f.parentConstructor.toString() + '::' + f.className;
150 function elementIsChildOf(el, potentialParent) {
151 if (el == potentialParent)
155 while (cur.parentNode) {
156 if (cur == potentialParent)
158 cur = cur.parentNode;
166 elementIsChildOf: elementIsChildOf