blob: 48bacc231ae8be49c9b293a2fa7cec357f4a0284 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2014 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.
-->
<link rel="import" href="/base/base.html">
<script>
'use strict';
tr.exportTo('tr.b.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 = tr.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.
*
* tr class:
* var List = tr.b.ui.define('list');
* List.prototype = {
* __proto__: HTMLUListElement.prototype,
* decorate: function() {
* ...
* },
* ...
* };
*
* Derived class:
* var CustomList = tr.b.ui.define('custom-list', List);
* CustomList.prototype = {
* __proto__: List.prototype,
* decorate: function() {
* ...
* },
* ...
* };
*
* @param {string} className The className of the newly created subtype. If
* subclassing by passing in opt_parentConstructor, 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 tr.b.ui.define.
*
* @param {string=} opt_tagNS The namespace in which to create the base
* element. Has no meaning when opt_parentConstructor is passed and must
* either be undefined or the same namespace as the parent class.
*
* @return {function(Object=):Element} The newly created component
* constructor.
*/
function define(className, opt_parentConstructor, opt_tagNS) {
if (typeof className == 'function') {
throw new Error('Passing functions as className is deprecated. Please ' +
'use (className, opt_parentConstructor) to subclass');
}
var className = className.toLowerCase();
if (opt_parentConstructor && !opt_parentConstructor.tagName)
throw new Error('opt_parentConstructor was not ' +
'created by tr.b.ui.define');
// Walk up the parent constructors until we can find the type of tag
// to create.
var tagName = className;
var tagNS = undefined;
if (opt_parentConstructor) {
if (opt_tagNS)
throw new Error('Must not specify tagNS if parentConstructor is given');
var parent = opt_parentConstructor;
while (parent && parent.tagName) {
tagName = parent.tagName;
tagNS = parent.tagNS;
parent = parent.parentConstructor;
}
} else {
tagNS = opt_tagNS;
}
/**
* 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(
className + ' prototye\'s __proto__ field is messed up. ' +
'It MUST be the prototype of ' + opt_parentConstructor.tagName);
}
var el;
if (tagNS === undefined)
el = tr.doc.createElement(tagName);
else
el = tr.doc.createElementNS(tagNS, tagName);
f.decorate.call(this, el, arguments);
return el;
}
/**
* 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.className = className;
f.tagName = tagName;
f.tagNS = tagNS;
f.parentConstructor = (opt_parentConstructor ? opt_parentConstructor :
undefined);
f.toString = function() {
if (!f.parentConstructor)
return f.tagName;
return f.parentConstructor.toString() + '::' + f.className;
};
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
};
});
</script>