Home | History | Annotate | Download | only in print_preview
      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.
      4 
      5 cr.define('print_preview', function() {
      6   'use strict';
      7 
      8   /**
      9    * Class that represents a UI component.
     10    * @constructor
     11    * @extends {cr.EventTarget}
     12    */
     13   function Component() {
     14     cr.EventTarget.call(this);
     15 
     16     /**
     17      * Component's HTML element.
     18      * @type {Element}
     19      * @private
     20      */
     21     this.element_ = null;
     22 
     23     this.isInDocument_ = false;
     24 
     25     /**
     26      * Component's event tracker.
     27      * @type {EventTracker}
     28      * @private
     29      */
     30      this.tracker_ = new EventTracker();
     31 
     32     /**
     33      * Child components of the component.
     34      * @type {Array.<print_preview.Component>}
     35      * @private
     36      */
     37     this.children_ = [];
     38   };
     39 
     40   Component.prototype = {
     41     __proto__: cr.EventTarget.prototype,
     42 
     43     /** Gets the component's element. */
     44     getElement: function() {
     45       return this.element_;
     46     },
     47 
     48     /** @return {EventTracker} Component's event tracker. */
     49     get tracker() {
     50       return this.tracker_;
     51     },
     52 
     53     /**
     54      * @return {boolean} Whether the element of the component is already in the
     55      *     HTML document.
     56      */
     57     get isInDocument() {
     58       return this.isInDocument_;
     59     },
     60 
     61     /**
     62      * Creates the root element of the component. Sub-classes should override
     63      * this method.
     64      */
     65     createDom: function() {
     66       this.element_ = cr.doc.createElement('div');
     67     },
     68 
     69     /**
     70      * Called when the component's element is known to be in the document.
     71      * Anything using document.getElementById etc. should be done at this stage.
     72      * Sub-classes should extend this method and attach listeners.
     73      */
     74     enterDocument: function() {
     75       this.isInDocument_ = true;
     76       this.children_.forEach(function(child) {
     77         if (!child.isInDocument && child.getElement()) {
     78           child.enterDocument();
     79         }
     80       });
     81     },
     82 
     83     /** Removes all event listeners. */
     84     exitDocument: function() {
     85       this.children_.forEach(function(child) {
     86         if (child.isInDocument) {
     87           child.exitDocument();
     88         }
     89       });
     90       this.tracker_.removeAll();
     91       this.isInDocument_ = false;
     92     },
     93 
     94     /**
     95      * Renders this UI component and appends the element to the given parent
     96      * element.
     97      * @param {!Element} parentElement Element to render the component's
     98      *     element into.
     99      */
    100     render: function(parentElement) {
    101       assert(!this.isInDocument, 'Component is already in the document');
    102       if (!this.element_) {
    103         this.createDom();
    104       }
    105       parentElement.appendChild(this.element_);
    106       this.enterDocument();
    107     },
    108 
    109     /**
    110      * Decorates an existing DOM element. Sub-classes should override the
    111      * override the decorateInternal method.
    112      * @param {Element} element Element to decorate.
    113      */
    114     decorate: function(element) {
    115       assert(!this.isInDocument, 'Component is already in the document');
    116       this.setElementInternal(element);
    117       this.decorateInternal();
    118       this.enterDocument();
    119     },
    120 
    121     /**
    122      * @param {print_preview.Component} child Component to add as a child of
    123      *     this component.
    124      */
    125     addChild: function(child) {
    126       this.children_.push(child);
    127     },
    128 
    129     /**
    130      * @param {!print_preview.Component} child Component to remove from this
    131      *     component's children.
    132      */
    133     removeChild: function(child) {
    134       var childIdx = this.children_.indexOf(child);
    135       if (childIdx != -1) {
    136         this.children_.splice(childIdx, 1);
    137       }
    138       if (child.isInDocument) {
    139         child.exitDocument();
    140         if (child.getElement()) {
    141           child.getElement().parentNode.removeChild(child.getElement());
    142         }
    143       }
    144     },
    145 
    146     /** Removes all of the component's children. */
    147     removeChildren: function() {
    148       while (this.children_.length > 0) {
    149         this.removeChild(this.children_[0]);
    150       }
    151     },
    152 
    153     /**
    154      * @param {string} query Selector query to select an element starting from
    155      *     the component's root element using a depth first search for the first
    156      *     element that matches the query.
    157      * @return {HTMLElement} Element selected by the given query.
    158      */
    159     getChildElement: function(query) {
    160       return this.element_.querySelector(query);
    161     },
    162 
    163     /**
    164      * Sets the component's element.
    165      * @param {Element} element HTML element to set as the component's element.
    166      * @protected
    167      */
    168     setElementInternal: function(element) {
    169       this.element_ = element;
    170     },
    171 
    172     /**
    173      * Decorates the given element for use as the element of the component.
    174      * @protected
    175      */
    176     decorateInternal: function() { /*abstract*/ },
    177 
    178     /**
    179      * Clones a template HTML DOM tree.
    180      * @param {string} templateId Template element ID.
    181      * @param {boolean=} opt_keepHidden Whether to leave the cloned template
    182      *     hidden after cloning.
    183      * @return {Element} Cloned element with its 'id' attribute stripped.
    184      * @protected
    185      */
    186     cloneTemplateInternal: function(templateId, opt_keepHidden) {
    187       var templateEl = $(templateId);
    188       assert(templateEl != null,
    189              'Could not find element with ID: ' + templateId);
    190       var el = templateEl.cloneNode(true);
    191       el.id = '';
    192       if (!opt_keepHidden) {
    193         setIsVisible(el, true);
    194       }
    195       return el;
    196     }
    197   };
    198 
    199   return {
    200     Component: Component
    201   };
    202 });
    203