Home | History | Annotate | Download | only in jstemplate
      1 // Copyright 2006 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 // http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
     12 // implied. See the License for the specific language governing
     13 // permissions and limitations under the License.
     14 /**
     15  * @fileoverview Miscellaneous constants and functions referenced in
     16  * the main source files.
     17  */
     18 
     19 function log(msg) {}
     20 
     21 // String literals defined globally and not to be inlined. (IE6 perf)
     22 /** @const */ var STRING_empty = '';
     23 
     24 /** @const */ var CSS_display = 'display';
     25 /** @const */ var CSS_position = 'position';
     26 
     27 // Constants for possible values of the typeof operator.
     28 var TYPE_boolean = 'boolean';
     29 var TYPE_number = 'number';
     30 var TYPE_object = 'object';
     31 var TYPE_string = 'string';
     32 var TYPE_function = 'function';
     33 var TYPE_undefined = 'undefined';
     34 
     35 
     36 /**
     37  * Wrapper for the eval() builtin function to evaluate expressions and
     38  * obtain their value. It wraps the expression in parentheses such
     39  * that object literals are really evaluated to objects. Without the
     40  * wrapping, they are evaluated as block, and create syntax
     41  * errors. Also protects against other syntax errors in the eval()ed
     42  * code and returns null if the eval throws an exception.
     43  *
     44  * @param {string} expr
     45  * @return {Object|null}
     46  */
     47 function jsEval(expr) {
     48   try {
     49     // NOTE(mesch): An alternative idiom would be:
     50     //
     51     //   eval('(' + expr + ')');
     52     //
     53     // Note that using the square brackets as below, "" evals to undefined.
     54     // The alternative of using parentheses does not work when evaluating
     55     // function literals in IE.
     56     // e.g. eval("(function() {})") returns undefined, and not a function
     57     // object, in IE.
     58     return eval('[' + expr + '][0]');
     59   } catch (e) {
     60     log('EVAL FAILED ' + expr + ': ' + e);
     61     return null;
     62   }
     63 }
     64 
     65 function jsLength(obj) {
     66   return obj.length;
     67 }
     68 
     69 function assert(obj) {}
     70 
     71 /**
     72  * Copies all properties from second object to the first.  Modifies to.
     73  *
     74  * @param {Object} to  The target object.
     75  * @param {Object} from  The source object.
     76  */
     77 function copyProperties(to, from) {
     78   for (var p in from) {
     79     to[p] = from[p];
     80   }
     81 }
     82 
     83 
     84 /**
     85  * @param {Object|null|undefined} value The possible value to use.
     86  * @param {Object} defaultValue The default if the value is not set.
     87  * @return {Object} The value, if it is
     88  * defined and not null; otherwise the default
     89  */
     90 function getDefaultObject(value, defaultValue) {
     91   if (typeof value != TYPE_undefined && value != null) {
     92     return /** @type Object */(value);
     93   } else {
     94     return defaultValue;
     95   }
     96 }
     97 
     98 /**
     99  * Detect if an object looks like an Array.
    100  * Note that instanceof Array is not robust; for example an Array
    101  * created in another iframe fails instanceof Array.
    102  * @param {Object|null} value Object to interrogate
    103  * @return {boolean} Is the object an array?
    104  */
    105 function isArray(value) {
    106   return value != null &&
    107       typeof value == TYPE_object &&
    108       typeof value.length == TYPE_number;
    109 }
    110 
    111 
    112 /**
    113  * Finds a slice of an array.
    114  *
    115  * @param {Array} array  Array to be sliced.
    116  * @param {number} start  The start of the slice.
    117  * @param {number} opt_end  The end of the slice (optional).
    118  * @return {Array} array  The slice of the array from start to end.
    119  */
    120 function arraySlice(array, start, opt_end) {
    121   // Use
    122   //   return Function.prototype.call.apply(Array.prototype.slice, arguments);
    123   // instead of the simpler
    124   //   return Array.prototype.slice.call(array, start, opt_end);
    125   // here because of a bug in the FF and IE implementations of
    126   // Array.prototype.slice which causes this function to return an empty list
    127   // if opt_end is not provided.
    128   return Function.prototype.call.apply(Array.prototype.slice, arguments);
    129 }
    130 
    131 
    132 /**
    133  * Jscompiler wrapper for parseInt() with base 10.
    134  *
    135  * @param {string} s string repersentation of a number.
    136  *
    137  * @return {number} The integer contained in s, converted on base 10.
    138  */
    139 function parseInt10(s) {
    140   return parseInt(s, 10);
    141 }
    142 
    143 
    144 /**
    145  * Clears the array by setting the length property to 0. This usually
    146  * works, and if it should turn out not to work everywhere, here would
    147  * be the place to implement the browser specific workaround.
    148  *
    149  * @param {Array} array  Array to be cleared.
    150  */
    151 function arrayClear(array) {
    152   array.length = 0;
    153 }
    154 
    155 
    156 /**
    157  * Prebinds "this" within the given method to an object, but ignores all
    158  * arguments passed to the resulting function.
    159  * I.e. var_args are all the arguments that method is invoked with when
    160  * invoking the bound function.
    161  *
    162  * @param {Object|null} object  The object that the method call targets.
    163  * @param {Function} method  The target method.
    164  * @return {Function}  Method with the target object bound to it and curried by
    165  *                     the provided arguments.
    166  */
    167 function bindFully(object, method, var_args) {
    168   var args = arraySlice(arguments, 2);
    169   return function() {
    170     return method.apply(object, args);
    171   }
    172 }
    173 
    174 // Based on <http://www.w3.org/TR/2000/ REC-DOM-Level-2-Core-20001113/
    175 // core.html#ID-1950641247>.
    176 var DOM_ELEMENT_NODE = 1;
    177 var DOM_ATTRIBUTE_NODE = 2;
    178 var DOM_TEXT_NODE = 3;
    179 var DOM_CDATA_SECTION_NODE = 4;
    180 var DOM_ENTITY_REFERENCE_NODE = 5;
    181 var DOM_ENTITY_NODE = 6;
    182 var DOM_PROCESSING_INSTRUCTION_NODE = 7;
    183 var DOM_COMMENT_NODE = 8;
    184 var DOM_DOCUMENT_NODE = 9;
    185 var DOM_DOCUMENT_TYPE_NODE = 10;
    186 var DOM_DOCUMENT_FRAGMENT_NODE = 11;
    187 var DOM_NOTATION_NODE = 12;
    188 
    189 
    190 
    191 function domGetElementById(document, id) {
    192   return document.getElementById(id);
    193 }
    194 
    195 /**
    196  * Creates a new node in the given document
    197  *
    198  * @param {Document} doc  Target document.
    199  * @param {string} name  Name of new element (i.e. the tag name)..
    200  * @return {Element}  Newly constructed element.
    201  */
    202 function domCreateElement(doc, name) {
    203   return doc.createElement(name);
    204 }
    205 
    206 /**
    207  * Traverses the element nodes in the DOM section underneath the given
    208  * node and invokes the given callback as a method on every element
    209  * node encountered.
    210  *
    211  * @param {Element} node  Parent element of the subtree to traverse.
    212  * @param {Function} callback  Called on each node in the traversal.
    213  */
    214 function domTraverseElements(node, callback) {
    215   var traverser = new DomTraverser(callback);
    216   traverser.run(node);
    217 }
    218 
    219 /**
    220  * A class to hold state for a dom traversal.
    221  * @param {Function} callback  Called on each node in the traversal.
    222  * @constructor
    223  * @class
    224  */
    225 function DomTraverser(callback) {
    226   this.callback_ = callback;
    227 }
    228 
    229 /**
    230  * Processes the dom tree in breadth-first order.
    231  * @param {Element} root  The root node of the traversal.
    232  */
    233 DomTraverser.prototype.run = function(root) {
    234   var me = this;
    235   me.queue_ = [ root ];
    236   while (jsLength(me.queue_)) {
    237     me.process_(me.queue_.shift());
    238   }
    239 }
    240 
    241 /**
    242  * Processes a single node.
    243  * @param {Element} node  The current node of the traversal.
    244  */
    245 DomTraverser.prototype.process_ = function(node) {
    246   var me = this;
    247 
    248   me.callback_(node);
    249 
    250   for (var c = node.firstChild; c; c = c.nextSibling) {
    251     if (c.nodeType == DOM_ELEMENT_NODE) {
    252       me.queue_.push(c);
    253     }
    254   }
    255 }
    256 
    257 /**
    258  * Get an attribute from the DOM.  Simple redirect, exists to compress code.
    259  *
    260  * @param {Element} node  Element to interrogate.
    261  * @param {string} name  Name of parameter to extract.
    262  * @return {string|null}  Resulting attribute.
    263  */
    264 function domGetAttribute(node, name) {
    265   return node.getAttribute(name);
    266   // NOTE(mesch): Neither in IE nor in Firefox, HTML DOM attributes
    267   // implement namespaces. All items in the attribute collection have
    268   // null localName and namespaceURI attribute values. In IE, we even
    269   // encounter DIV elements that don't implement the method
    270   // getAttributeNS().
    271 }
    272 
    273 
    274 /**
    275  * Set an attribute in the DOM.  Simple redirect to compress code.
    276  *
    277  * @param {Element} node  Element to interrogate.
    278  * @param {string} name  Name of parameter to set.
    279  * @param {string|number} value  Set attribute to this value.
    280  */
    281 function domSetAttribute(node, name, value) {
    282   node.setAttribute(name, value);
    283 }
    284 
    285 /**
    286  * Remove an attribute from the DOM.  Simple redirect to compress code.
    287  *
    288  * @param {Element} node  Element to interrogate.
    289  * @param {string} name  Name of parameter to remove.
    290  */
    291 function domRemoveAttribute(node, name) {
    292   node.removeAttribute(name);
    293 }
    294 
    295 /**
    296  * Clone a node in the DOM.
    297  *
    298  * @param {Node} node  Node to clone.
    299  * @return {Node}  Cloned node.
    300  */
    301 function domCloneNode(node) {
    302   return node.cloneNode(true);
    303   // NOTE(mesch): we never so far wanted to use cloneNode(false),
    304   // hence the default.
    305 }
    306 
    307 /**
    308  * Clone a element in the DOM.
    309  *
    310  * @param {Element} element  Element to clone.
    311  * @return {Element}  Cloned element.
    312  */
    313 function domCloneElement(element) {
    314   return /** @type {Element} */(domCloneNode(element));
    315 }
    316 
    317 /**
    318  * Returns the document owner of the given element. In particular,
    319  * returns window.document if node is null or the browser does not
    320  * support ownerDocument.  If the node is a document itself, returns
    321  * itself.
    322  *
    323  * @param {Node|null|undefined} node  The node whose ownerDocument is required.
    324  * @returns {Document}  The owner document or window.document if unsupported.
    325  */
    326 function ownerDocument(node) {
    327   if (!node) {
    328     return document;
    329   } else if (node.nodeType == DOM_DOCUMENT_NODE) {
    330     return /** @type Document */(node);
    331   } else {
    332     return node.ownerDocument || document;
    333   }
    334 }
    335 
    336 /**
    337  * Creates a new text node in the given document.
    338  *
    339  * @param {Document} doc  Target document.
    340  * @param {string} text  Text composing new text node.
    341  * @return {Text}  Newly constructed text node.
    342  */
    343 function domCreateTextNode(doc, text) {
    344   return doc.createTextNode(text);
    345 }
    346 
    347 /**
    348  * Appends a new child to the specified (parent) node.
    349  *
    350  * @param {Element} node  Parent element.
    351  * @param {Node} child  Child node to append.
    352  * @return {Node}  Newly appended node.
    353  */
    354 function domAppendChild(node, child) {
    355   return node.appendChild(child);
    356 }
    357 
    358 /**
    359  * Sets display to default.
    360  *
    361  * @param {Element} node  The dom element to manipulate.
    362  */
    363 function displayDefault(node) {
    364   node.style[CSS_display] = '';
    365 }
    366 
    367 /**
    368  * Sets display to none. Doing this as a function saves a few bytes for
    369  * the 'style.display' property and the 'none' literal.
    370  *
    371  * @param {Element} node  The dom element to manipulate.
    372  */
    373 function displayNone(node) {
    374   node.style[CSS_display] = 'none';
    375 }
    376 
    377 
    378 /**
    379  * Sets position style attribute to absolute.
    380  *
    381  * @param {Element} node  The dom element to manipulate.
    382  */
    383 function positionAbsolute(node) {
    384   node.style[CSS_position] = 'absolute';
    385 }
    386 
    387 
    388 /**
    389  * Inserts a new child before a given sibling.
    390  *
    391  * @param {Node} newChild  Node to insert.
    392  * @param {Node} oldChild  Sibling node.
    393  * @return {Node}  Reference to new child.
    394  */
    395 function domInsertBefore(newChild, oldChild) {
    396   return oldChild.parentNode.insertBefore(newChild, oldChild);
    397 }
    398 
    399 /**
    400  * Replaces an old child node with a new child node.
    401  *
    402  * @param {Node} newChild  New child to append.
    403  * @param {Node} oldChild  Old child to remove.
    404  * @return {Node}  Replaced node.
    405  */
    406 function domReplaceChild(newChild, oldChild) {
    407   return oldChild.parentNode.replaceChild(newChild, oldChild);
    408 }
    409 
    410 /**
    411  * Removes a node from the DOM.
    412  *
    413  * @param {Node} node  The node to remove.
    414  * @return {Node}  The removed node.
    415  */
    416 function domRemoveNode(node) {
    417   return domRemoveChild(node.parentNode, node);
    418 }
    419 
    420 /**
    421  * Remove a child from the specified (parent) node.
    422  *
    423  * @param {Element} node  Parent element.
    424  * @param {Node} child  Child node to remove.
    425  * @return {Node}  Removed node.
    426  */
    427 function domRemoveChild(node, child) {
    428   return node.removeChild(child);
    429 }
    430 
    431 
    432 /**
    433  * Trim whitespace from begin and end of string.
    434  *
    435  * @see testStringTrim();
    436  *
    437  * @param {string} str  Input string.
    438  * @return {string}  Trimmed string.
    439  */
    440 function stringTrim(str) {
    441   return stringTrimRight(stringTrimLeft(str));
    442 }
    443 
    444 /**
    445  * Trim whitespace from beginning of string.
    446  *
    447  * @see testStringTrimLeft();
    448  *
    449  * @param {string} str  Input string.
    450  * @return {string}  Trimmed string.
    451  */
    452 function stringTrimLeft(str) {
    453   return str.replace(/^\s+/, "");
    454 }
    455 
    456 /**
    457  * Trim whitespace from end of string.
    458  *
    459  * @see testStringTrimRight();
    460  *
    461  * @param {string} str  Input string.
    462  * @return {string}  Trimmed string.
    463   */
    464 function stringTrimRight(str) {
    465   return str.replace(/\s+$/, "");
    466 }
    467