1 /*
  2  *  Copyright © 2009 Apple Inc. All rights reserved.
  3  */
  4 
  5 /**
  6  *  @class
  7  *  @name Element
  8  *  @description Extensions to the DOM Core <a href="http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-745549614"><code>Element</code></a> interface.
  9  *  @since TuneKit 1.0
 10  */
 11 
 12 /* ==================== Element Extensions ==================== */
 13 
 14 /**
 15  *  Indicates whether the element has a given class name within its <code>class</code> attribute.
 16  *
 17  *  @param {String} className The CSS class name.
 18  *  @returns {bool} Whether the element has this class name within its <code>class</code> attribute.
 19  *  @memberOf Element.prototype
 20  */
 21 Element.prototype.hasClassName = function (className) {
 22   return new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)').test(this.className);
 23 };
 24 
 25 /**
 26  *  Adds the given class name to the element's <code>class</code> attribute if it's not already there.
 27  *
 28  *  @param {String} className The CSS class name.
 29  *  @memberOf Element.prototype
 30  */
 31 Element.prototype.addClassName = function (className) {
 32   if (!this.hasClassName(className)) {
 33     this.className = [this.className, className].join(' ');
 34   }
 35 };
 36 
 37 /**
 38  *  Removes the given class name from the element's <code>class</code> attribute if it's there.
 39  *
 40  *  @param {String} className The CSS class name.
 41  *  @memberOf Element.prototype
 42  */
 43 Element.prototype.removeClassName = function (className) {
 44   if (this.hasClassName(className)) {
 45     var curClasses = this.className;
 46     this.className = curClasses.replace(new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g'), ' ');
 47   }
 48 };
 49 
 50 /**
 51  *  Adds the given class name to the element's <code>class</code> attribute if it's not there, or removes it if it's already set.
 52  *
 53  *  @param {String} className The CSS class name.
 54  *  @memberOf Element.prototype
 55  */
 56 Element.prototype.toggleClassName = function (className) {
 57   this[this.hasClassName(className) ? 'removeClassName' : 'addClassName'](className);
 58 };
 59 
 60 /**
 61  *  Removes all the children from an element.
 62  *
 63  *  @memberOf Element.prototype
 64  */
 65 // XXX: should this be on Node?
 66 Element.prototype.removeAllChildren = function () {
 67   while (this.firstChild) {
 68     this.removeChild(this.firstChild);
 69   }
 70 };
 71 
 72 /**
 73  *  Returns true if the element has the given child node
 74  *  FIXME: should this be on Node?
 75  *
 76  *  @param {Element} child The child to search for
 77  *  @memberOf Element.prototype
 78  */
 79 Element.prototype.hasChild = function (child) {
 80   for (var i=0; i < this.childNodes.length; i++) {
 81     if (this.childNodes[i] === child) {
 82       return true;
 83     }
 84   }
 85   return false;
 86 };
 87 
 88 /**
 89  *  Applies a transition definition to the element, allowing the transition to be reversed. If this method is called
 90  *  within a {@link TKTransaction}, the transition will only be commited when the transaction is completed.
 91  *
 92  *  @param {TKTransitionDefinition} transitionDefinition The transition applied to the element.
 93  *  @param {bool} reversed Indicates whether the transition is to be applied in reverse.
 94  */
 95 Element.prototype.applyTransition = function (definition, reversed) {
 96   // nothing to do if we have no definition
 97   if (definition === null) {
 98     return;
 99   }
100   // create a TKTransition from the definition
101   var transition = new TKTransition(definition);
102   // this view will be the target
103   transition.target = this;
104   // revert from / to values as instructed
105   if (reversed) {
106     var from = transition.from;
107     transition.from = transition.to;
108     transition.to = from;
109   }
110   // set up base properties, if any
111   if (definition.base) {
112     for (var i = 0; i < definition.base.length; i += 2) {
113       this.style.setProperty(definition.base[i], definition.base[i+1], '');
114     }
115   }
116   // start the transition
117   transition.start();
118 };
119 
120 Element.prototype.getBounds = function () {
121   return TKRect.rectFromClientRect(this.getBoundingClientRect());
122 };
123 
124 Element.prototype.isNavigable = function () {
125   var is_navigable = false;
126   if (this._controller !== undefined && this._controller !== null && this._controller.navigableElements.indexOf(this) !== -1) {
127     var style = window.getComputedStyle(this);
128     is_navigable = (
129       style.display != 'none' && style.visibility != 'hidden' &&
130       !this.hasClassName(TKSpatialNavigationManagerInactiveCSSClass)
131     );
132   }
133   return is_navigable;
134 };
135 
136 TKUtils.setupDisplayNames(Element, 'Element');
137