/* This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/ /** @module montage/ui/component @requires montage/core/core @requires montage/ui/reel @requires montage/core/gate @requires montage/core/logger | component @requires montage/core/logger | drawing @requires montage/core/event/event-manager */ var Montage = require("montage").Montage, Template = require("ui/template").Template, Gate = require("core/gate").Gate, logger = require("core/logger").logger("component"), drawLogger = require("core/logger").logger("drawing"), defaultEventManager = require("core/event/event-manager").defaultEventManager; /** * @class module:montage/ui/component.Component * @classdesc Base class for all Montage components. @extends module:montage/core/core.Montage */ var Component = exports.Component = Montage.create(Montage,/** @lends module:montage/ui/component.Component# */ { /** Description TODO @type {Property} @default null */ delegate: { serializable: "reference", value: null }, parentProperty: { serializable: true, value: "parentComponent" }, /** Dispatch the actionEvent this component is configured to emit upon interaction @private */ _dispatchActionEvent: { value: function() { this.dispatchEvent(this.createActionEvent()); }, enumerable: false }, /** Create a custom event to dispatch upon interaction @type {Function} @returns and event to dispatch upon interaction */ createActionEvent: { value: function() { var actionEvent = document.createEvent("CustomEvent"); actionEvent.initCustomEvent("action", true, true, null); return actionEvent; } }, /** Description TODO @function @returns this._canDrawGate */ canDrawGate: { get: function() { if (!this._canDrawGate) { this._canDrawGate = Gate.create().initWithDelegate(this); this._canDrawGate.setField("componentTreeLoaded", false); } return this._canDrawGate; } }, /** Description TODO @private */ _blockDrawGate: { value: null }, /** Description TODO @function @returns this._blockDrawGate */ blockDrawGate: { get: function() { if (!this._blockDrawGate) { this._blockDrawGate = Gate.create().initWithDelegate(this); this._blockDrawGate.setField("element", false); this._blockDrawGate.setField("drawRequested", false); } return this._blockDrawGate; } }, /** Description TODO @private */ _firstDraw: { enumerable: false, value: true }, /** Description TODO @private */ _completedFirstDraw: { enumerable: false, value: false }, /** Description TODO @private */ _element: { enumerable: false, value: null }, /** Description TODO @type {Function} @default null */ element: { serializable: true, enumerable: true, get: function() { return this._element; }, set: function(value) { if (value == null) { console.warn("Tried to set element of ", this, " to ", value); return; } if (this.isDeserializing) { this.eventManager.registerEventHandlerForElement(this, value); // if this component has a template and has been already instantiated then assume the value is the template. if (this._isTemplateInstantiated) { // this is important for component extension, we don't want to override template element if (!this._templateElement) { this._templateElement = value; } } else { this._element = value; if (!this.blockDrawGate.value && this._element) { this.blockDrawGate.setField("element", true); } } } else if (!this._firstDraw) { // If a draw has happened then at some point the element has been set console.error("Cannot change element of ", this, " after it has been set"); return; } else { this.eventManager.registerEventHandlerForElement(this, value); this._element = value; if (!this.blockDrawGate.value && this._element) { this.blockDrawGate.setField("element", true); } } } }, setElementWithParentComponent: { value: function(element, parent) { this._alternateParentComponent = parent; if (this.element != element) { this.element = element; } } }, // access to the Application object /** Description TODO @function @returns document.application */ application: { get: function() { return document.application; } }, /** Description TODO @function @returns defaultEventManager */ eventManager: { get: function() { return defaultEventManager; } }, /** Description TODO @function @returns rootComponent */ rootComponent: { get: function() { return rootComponent; } }, /** Description TODO @function @returns {Boolean} false */ acceptsDirectFocus: { enumerable: false, value: function() { return false; } }, /** Description TODO @function @returns targetElementController */ elementControllerFromEvent: { enumerable: false, value: function(event, targetElementController) { return targetElementController; } }, _alternateParentComponent: { value: null }, /** Description TODO @private */ _cachedParentComponent: { value: null }, // TODO store the value and delete it after draw /** The parent component is found by walking up the DOM tree from the node returned by the element property.
If we find a parentNode that has a controller then we return this controller.
Returns undefined if this is the rootComponent. @function @returns undefined or cachedParentComponent */ parentComponent: { enumerable: false, get: function() { var cachedParentComponent = this._cachedParentComponent; if (cachedParentComponent == null) { return (this._cachedParentComponent = this.findParentComponent()); } else { return cachedParentComponent; } } }, findParentComponent: { value: function() { var anElement = this.element, aParentNode, eventManager = this.eventManager; if (anElement) { while ((aParentNode = anElement.parentNode) != null && eventManager.eventHandlerForElement(aParentNode) == null) { anElement = aParentNode; } return aParentNode ? eventManager.eventHandlerForElement(aParentNode) : this._alternateParentComponent; } } }, querySelectorComponent: { value: function(selector) { if (typeof selector !== "string") { throw "querySelectorComponent: Selector needs to be a string."; } // \s*(?:@([^>\s]+)) leftHandOperand [