// 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; } /** * 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]); }; 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 }; });