You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by mh...@apache.org on 2011/05/09 22:52:09 UTC

svn commit: r1101218 - in /shindig/trunk/features/src/main/javascript/features/core.util: feature.xml util.js

Author: mhermanto
Date: Mon May  9 20:52:09 2011
New Revision: 1101218

URL: http://svn.apache.org/viewvc?rev=1101218&view=rev
Log:
XHTML-capable DOM utilities.
http://codereview.appspot.com/4506043/

Modified:
    shindig/trunk/features/src/main/javascript/features/core.util/feature.xml
    shindig/trunk/features/src/main/javascript/features/core.util/util.js

Modified: shindig/trunk/features/src/main/javascript/features/core.util/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml?rev=1101218&r1=1101217&r2=1101218&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/core.util/feature.xml (original)
+++ shindig/trunk/features/src/main/javascript/features/core.util/feature.xml Mon May  9 20:52:09 2011
@@ -39,6 +39,9 @@
       <exports type="js">gadgets.util.unescapeString</exports>
       <exports type="js">gadgets.util.attachBrowserEvent</exports>
       <exports type="js">gadgets.util.removeBrowserEvent</exports>
+      <exports type="js">gadgets.util.createElement</exports>
+      <exports type="js">gadgets.util.createIframeElement</exports>
+      <exports type="js">gadgets.util.getBodyElement</exports>
     </api>
   </all>
 </feature>

Modified: shindig/trunk/features/src/main/javascript/features/core.util/util.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/core.util/util.js?rev=1101218&r1=1101217&r2=1101218&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/core.util/util.js (original)
+++ shindig/trunk/features/src/main/javascript/features/core.util/util.js Mon May  9 20:52:09 2011
@@ -34,6 +34,31 @@ gadgets.util = function() {
   var services = {};
   var onLoadHandlers = [];
 
+  var XHTML_SPEC = 'http://www.w3.org/1999/xhtml';
+
+  function attachAttributes(elem, opt_attribs) {
+    var attribs = opt_attribs || {};
+    for (var attrib in attribs) {
+      if (attribs.hasOwnProperty(attrib)) {
+        elem[attrib] = attribs[attrib];
+      }
+    }
+  }
+
+  function stringifyElement(tagName, opt_attribs) {
+    var arr = [];
+    arr.push('<').push(tagName);
+    var attribs = opt_attribs || {};
+    for (var attrib in attribs) {
+      if (attribs.hasOwnProperty(attrib)) {
+        var value = escapeString(attribs[attrib]);
+        arr.push(' ').push(attrib).push('="').push(value).push('"');
+      }
+    }
+    arr.push('></').push(tagName).push('>');
+    return arr.join('');
+  }
+
   /**
    * @enum {boolean}
    * @const
@@ -57,12 +82,22 @@ gadgets.util = function() {
     60 : true,
     // greater than
     62 : true,
-    // Backslash
+    // backslash
     92 : true,
     // line separator
     8232 : true,
     // paragraph separator
-    8233 : true
+    8233 : true,
+    // fullwidth quotation mark
+    65282 : true,
+    // fullwidth apostrophe
+    65287 : true,
+    // fullwidth less-than sign
+    65308 : true,
+    // fullwidth greater-than sign
+    65310 : true,
+    // fullwidth reverse solidus
+    65340 : true
   };
 
   /**
@@ -73,9 +108,44 @@ gadgets.util = function() {
    * @return {string} The character corresponding to value.
    */
   function unescapeEntity(match, value) {
+    // TODO: b0rked for UTF-16 and can easily be convinced to generate
+    // truncating NULs or completely invalid non-Unicode characters. Here's a
+    // fixed version (it handles entities for valid codepoints from U+0001 ...
+    // U+10FFFD, except for the non-character codepoints U+...FFFE and
+    // U+...FFFF; isolated UTF-16 surrogate pairs are supported for
+    // compatibility with previous versions of escapeString, 0 generates the
+    // empty string rather than a possibly-truncating '\0', and all other inputs
+    // generate U+FFFD (the replacement character, standard practice for
+    // non-signalling Unicode codecs like this one)
+    //     return (
+    //         (value > 0) &&
+    //         (value <= 0x10fffd) &&
+    //         ((value & 0xffff) < 0xfffe)) ?
+    //       ((value <= 0xffff) ?
+    //         String.fromCharCode(value) :
+    //         String.fromCharCode(
+    //           ((value - 0x10000) >> 10) | 0xd800,
+    //           ((value - 0x10000) & 0x3ff) | 0xdc00)) :
+    //       ((value === 0) ? '' : '\ufffd');
     return String.fromCharCode(value);
   }
 
+  function escapeString(str) {
+    if (!str) return str;
+    var out = [], ch, shouldEscape;
+    for (var i = 0, j = str.length; i < j; ++i) {
+      ch = str.charCodeAt(i);
+      shouldEscape = escapeCodePoints[ch];
+      if (shouldEscape === true) {
+        out.push('&#', ch, ';');
+      } else if (shouldEscape !== false) {
+        // undefined or null are OK.
+        out.push(str.charAt(i));
+      }
+    }
+    return out.join('');
+  }
+
   /**
    * Initializes feature parameters.
    */
@@ -247,27 +317,10 @@ gadgets.util = function() {
      * Currently not in the spec -- future proposals may change
      * how this is handled.
      *
-     * TODO: Parsing the string would probably be more accurate and faster than
-     * a bunch of regular expressions.
-     *
      * @param {string} str The string to escape.
      * @return {string} The escaped string.
      */
-    escapeString : function(str) {
-      if (!str) return str;
-      var out = [], ch, shouldEscape;
-      for (var i = 0, j = str.length; i < j; ++i) {
-        ch = str.charCodeAt(i);
-        shouldEscape = escapeCodePoints[ch];
-        if (shouldEscape === true) {
-          out.push('&#', ch, ';');
-        } else if (shouldEscape !== false) {
-          // undefined or null are OK.
-          out.push(str.charAt(i));
-        }
-      }
-      return out.join('');
-    },
+    escapeString : escapeString,
 
     /**
      * Reverses escapeString
@@ -280,7 +333,6 @@ gadgets.util = function() {
       return str.replace(/&#([0-9]+);/g, unescapeEntity);
     },
 
-
     /**
      * Attach an event listener to given DOM element (Not a gadget standard)
      *
@@ -316,6 +368,73 @@ gadgets.util = function() {
       } else {
         gadgets.warn('cannot removeBrowserEvent: ' + eventName);
       }
+    },
+
+    /**
+     * Creates an HTML or XHTML element.
+     * @param {string} tagName The type of element to construct.
+     * @return {Element} The newly constructed element.
+     */
+    createElement : function(tagName) {
+      // TODO: factor this out to core.util.dom.
+      var element;
+      if ((!document.body) || document.body.namespaceURI) {
+        try {
+          element = document.createElementNS(XHTML_SPEC, tagName);
+        } catch (nonXmlDomException) {
+        }
+      }
+      return element || document.createElement(tagName);
+    },
+
+    /**
+     * Creates an HTML or XHTML iframe element with attributes.
+     * @param {Object=} opt_attribs Optional set of attributes to attach. The
+     * only working attributes are spelled the same way in XHTML attribute
+     * naming (most strict, all-lower-case), HTML attribute naming (less strict,
+     * case-insensitive), and JavaScript property naming (some properties named
+     * incompatibly with XHTML/HTML).
+     * @return {Element} The DOM node representing body.
+     */
+    createIframeElement : function(opt_attribs) {
+      // TODO: factor this out to core.util.dom.
+      var frame = gadgets.util.createElement('iframe');
+      try {
+        // TODO: provide automatic mapping to only set the needed
+        // and JS-HTML-XHTML compatible subset through stringifyElement (just
+        // 'name' and 'id', AFAIK). The values of the attributes will be
+        // stringified should the stringifyElement code path be taken (IE)
+        var tagString = stringifyElement('iframe', opt_attribs);
+        var ieFrame = gadgets.util.createElement(tagString);
+        if (ieFrame &&
+            ((!frame) ||
+             ((ieFrame.tagName == frame.tagName) &&
+              (ieFrame.namespaceURI == frame.namespaceURI)))) {
+          frame = ieFrame;
+        }
+      } catch (nonStandardCallFailed) {
+      }
+      attachAttributes(frame, opt_attribs);
+      return frame;
+    },
+
+    /**
+     * Gets the HTML or XHTML body element.
+     * @return {Element} The DOM node representing body.
+     */
+    getBodyElement : function() {
+      // TODO: factor this out to core.util.dom.
+      if (document.body) {
+        return document.body;
+      }
+      try {
+        var xbodies = document.getElementsByTagNameNS(XHTML_SPEC, 'body');
+        if (xbodies && (xbodies.length == 1)) {
+          return xbodies[0];
+        }
+      } catch (nonXmlDomException) {
+      }
+      return document.documentElement || document;
     }
   };
 }();