ericduran/chromeHAR

View on GitHub
bower_components/json3/benchmark/assets/html5.js

Summary

Maintainability
B
5 hrs
Test Coverage
/*!
 * HTML5.js v1.0.0-rc
 * Copyright 2012 John-David Dalton <http://allyoucanleet.com/>
 * Based on HTML5 Shiv vpre3.3 | @afarkas @jon_neal @rem | MIT/GPL2 Licensed
 * Available under MIT/GPL2 license
 */
;(function(window, document) {
  'use strict';

  /** Preset for the install/uninstall methods */
  var allOptions = { 'methods': true, 'print': true, 'styles': true };

  /** Cache of created elements, document methods, and install state */
  var html5Cache = {};

  /** Previous `html5` object */
  var old = window.html5;

  /** List of HTML5 node names to install support for */
  var nodeNames = [
    'abbr', 'article', 'aside', 'audio', 'bdi', 'canvas', 'data', 'datalist',
    'details', 'figcaption', 'figure', 'footer', 'header', 'main',
    'mark', 'meter', 'nav', 'output', 'progress', 'section', 'summary', 'time',
    'video'
  ];

  /** Used to namespace printable elements and define `expando` */
  var namespace = 'html5js';

  /** Used to store an elements `uid` if `element.uniqueNumber` is not supported */
  var expando = namespace + /\d+$/.exec(Math.random());

  /** Used to filter media types */
  var reMedia = /^$|\b(?:all|print)\b/;

  /**
   * Used to skip elements with type attributes because in IE they cannot be
   * set/changed once an element is inserted into a document/fragment.
   * http://msdn.microsoft.com/en-us/library/ie/ms534700(v=vs.85).aspx
   */
  var reSkip = /^(?:button|select)$/i;

  /** Used to detect elements that cannot be cloned correctly */
  var reUnclonable = /^<\?/;

  /** Used as a fallback for `element.uniqueNumber` */
  var uid = 1;

  /** Cache of unclonable element node names */
  var unclonables = {};

  /**
   * An object used to flag features.
   *
   * @static
   * @memberOf html5
   * @type Object
   */
  var support = {};

  (function() {
    var p,
        parent,
        sandbox;

    // create a new document used to get untainted styles
    try {
      // avoid https: protocol issues with IE
      sandbox = new ActiveXObject(location.protocol == 'https:' && 'htmlfile');
    } catch(e) {
      // http://xkr.us/articles/dom/iframe-document/
      (sandbox = document.createElement('iframe')).name = expando;
      sandbox.frameBorder = sandbox.height = sandbox.width = 0;
      parent = document.body || document.documentElement;
      parent.insertBefore(sandbox, parent.firstChild);
      sandbox = (sandbox = sandbox.contentWindow || sandbox.contentDocument || frames[expando]).document || sandbox;
    }
    sandbox.write('<!doctype html><title></title><body><script>document.w = this<\/script>');
    sandbox.close();

    p = sandbox.body.appendChild(sandbox.createElement('p'));
    p.innerHTML = '<nav/>';

    /**
     * Detect whether the browser supports default HTML5 styles.
     * @memberOf html5.support
     * @type Boolean
     */
    support.html5Styles = !!p.firstChild &&
      (p.firstChild.currentStyle || sandbox.w.getComputedStyle(p.firstChild, null)).display == 'block';

    /**
     * Detect whether the browser supports unknown elements.
     *
     * @memberOf html5.support
     * @type Boolean
     */
    support.unknownElements = p.childNodes.length == 1 || (function() {
      // assign a false positive if unable to install
      try {
        (document.createElement)('p');
      } catch(e) {
        return true;
      }
      var frag = document.createDocumentFragment();
      return (
        typeof frag.createElement == 'undefined' ||
        typeof p.uniqueNumber == 'undefined'
      );
    }());

    /**
     * Detect whether the browser supports printing html5 elements.
     *
     * @memberOf html5.support
     * @type Boolean
     */
    support.html5Printing = support.unknownElements || (
      // assign a false positive if unable to install
      typeof document.namespaces == 'undefined' ||
      typeof document.parentWindow == 'undefined' ||
      typeof p.applyElement == 'undefined' ||
      typeof p.removeNode == 'undefined' ||
      typeof window.attachEvent == 'undefined'
    );

    parent && destroyElement(sandbox.w.frameElement);
  }());

  /*--------------------------------------------------------------------------*/

  /**
   * Creates a style sheet of modified CSS rules to style the print wrappers.
   * (e.g. the CSS rule "header{}" becomes "html5js\:header{}")
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @param {String} cssText The CSS text.
   * @returns {StyleSheet} The style element.
   */
  function addPrintSheet(ownerDocument, cssText) {
    var pair,
        parts = cssText.split('{'),
        index = parts.length,
        reElements = RegExp('(^|[\\s,>+~])(' + nodeNames.join('|') + ')(?=[[\\s,>+~#.:]|$)', 'gi'),
        replacement = '$1' + namespace + '\\:$2';

    while (index--) {
      pair = parts[index] = parts[index].split('}');
      pair[pair.length - 1] = pair[pair.length - 1].replace(reElements, replacement);
      parts[index] = pair.join('}');
    }
    return addStyleSheet(ownerDocument, parts.join('{'));
  }

  /**
   * Wraps all HTML5 elements in the given document with printable elements.
   * (e.g. the "header" element is wrapped with the "html5js:header" element)
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @returns {Array} An array of added wrappers.
   */
  function addPrintWrappers(ownerDocument) {
    var node,
        nodes = ownerDocument.getElementsByTagName('*'),
        index = nodes.length,
        reElements = RegExp('^(?:' + nodeNames.join('|') + ')$', 'i'),
        result = [];

    while (index--) {
      node = nodes[index];
      if (reElements.test(node.nodeName)) {
        result.push(node.applyElement(createPrintWrapper(node)));
      }
    }
    return result;
  }

  /**
   * Creates a style sheet with the given CSS text and adds it to the document.
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @param {String} cssText The CSS text.
   * @returns {StyleSheet} The style sheet.
   */
  function addStyleSheet(ownerDocument, cssText) {
    // IE8 only respects namespace prefixs when created with `innerHTML`
    var p = ownerDocument.createElement('p'),
        parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;

    p.innerHTML = 'x<style>' + cssText + '</style>';
    return parent.insertBefore(p.lastChild, parent.firstChild);
  }

  /**
   * Creates HTML5 elements using the given document enabling the document to
   * parse them correctly.
   *
   * @private
   * @param {Document|Fragment} ownerDocument The document.
   * @returns {Document|Fragment} The document.
   */
  function createElements(ownerDocument) {
    var index = nodeNames.length,
        create = ownerDocument.createElement;

    while (index--) {
      create(nodeNames[index]);
    }
    return ownerDocument;
  }

  /**
   * Creates a printable wrapper for the given element.
   *
   * @private
   * @param {Element} element The element.
   * @returns {Element} The wrapper.
   */
  function createPrintWrapper(element) {
    var node,
        nodes = element.attributes,
        index = nodes.length,
        wrapper = element.ownerDocument.createElement(namespace + ':' + element.nodeName);

    // copy element attributes to the wrapper
    while (index--) {
      node = nodes[index];
      node.specified && wrapper.setAttribute(node.nodeName, node.nodeValue);
    }
    // copy element styles to the wrapper
    wrapper.style.cssText = element.style.cssText;
    return wrapper;
  }

  /**
   * Destroys the given element.
   *
   * @private
   * @param {Element} element The element to destroy.
   * @param {Object} [cache] The cache object.
   */
  function destroyElement(element, cache) {
    var trash = (cache || getCache(element.ownerDocument)).trash;
    trash.appendChild(element);
    trash.innerHTML = '';
  }

  /**
   * Gets the cache object for the given document.
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @returns {Object} The cache object.
   */
  function getCache(ownerDocument) {
    var docEl = ownerDocument.documentElement,
        id = docEl.uniqueNumber || docEl[expando] || (docEl[expando] = uid++),
        skip = support.unknownElements;

    return html5Cache[id] || (html5Cache[id] = {
      'frag': !skip && createElements(ownerDocument.createDocumentFragment()),
      'nativeCreateElement': !skip && createElements(ownerDocument).createElement,
      'nativeCreateFragment': !skip && ownerDocument.createDocumentFragment,
      'nodes': {},
      'trash': ownerDocument.createElement('div')
    });
  }

  /**
   * Removes the given print wrappers, leaving the original elements.
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @params {Array} wrappers An array of wrappers.
   */
  function removePrintWrappers(ownerDocument, wrappers) {
    var cache = getCache(ownerDocument),
        index = wrappers.length;

    while (index--) {
      destroyElement(wrappers[index].removeNode(), cache);
    }
  }

  /**
   * Resolves an options object from the given value.
   *
   * @private
   * @param {Mixed} value The value to convert to an options object.
   * @returns {Object} The options object.
   */
  function resolveOptions(value) {
    var key;
    value = value ? (value === 'all' || value.all ? allOptions : value) : {};

    if (typeof value == 'string') {
      var object = {};
      value = value.split(/[, ]+/);
      while ((key = value.pop())) {
        object[key] = true;
      }
      value = object;
    }
    return value;
  }

  /**
   * Overwrites the document's `createElement` and `createDocumentFragment` methods
   * with `html5.createElement` and `html5.createDocumentFragment` equivalents.
   *
   * @private
   * @param {Document} ownerDocument The document.
   */
  function setMethods(ownerDocument) {
    var cache = getCache(ownerDocument),
        create = cache.nativeCreateElement,
        frag = cache.frag,
        nodes = cache.nodes;

    // allow a small amount of repeated code for better performance
    ownerDocument.createElement = function(nodeName) {
      var cached = nodes[nodeName],
          node = cached ? cached.cloneNode() : create(nodeName);

      if (!cached && !unclonables[nodeName] &&
          !(unclonables[nodeName] = reUnclonable.test(node.outerHTML))) {
        node = (nodes[nodeName] = node).cloneNode();
      }
      return node.canHaveChildren && !reSkip.test(nodeName) ? frag.appendChild(node) : node;
    };

    // compile unrolled `createElement` calls for better performance
    ownerDocument.createDocumentFragment = Function('frag',
      'return function() {\n' +
      '  var node = frag.cloneNode(), create = node.createElement;\n' +
      (nodeNames + '').replace(/\w+/g, 'create("$&")\n') + ';\n' +
      '  return node\n' +
      '}'
    )(frag);
  }

  /**
   * Adds support for printing HTML5 elements.
   *
   * @private
   * @param {Document} ownerDocument The document.
   */
  function setPrintSupport(ownerDocument) {
    var printSheet,
        wrappers,
        cache = getCache(ownerDocument),
        namespaces = ownerDocument.namespaces,
        ownerWindow = ownerDocument.parentWindow;

    ownerWindow.attachEvent('onbeforeprint', cache.onbeforeprint = function() {
      var imports,
          length,
          sheet,
          collection = ownerDocument.styleSheets,
          cssText = [],
          index = collection.length,
          sheets = [];

      // convert styleSheets collection to an array
      while (index--) {
        sheets[index] = collection[index];
      }
      // concat all style sheet CSS text
      while ((sheet = sheets.pop())) {
        // IE does not enforce a same origin policy for external style sheets...
        if (!sheet.disabled && reMedia.test(sheet.media)) {
          // ...but will throw an "access denied" error when attempting to read
          // the CSS text of a style sheet added by a script from a different origin.
          try {
            cssText.push(sheet.cssText);
            for (imports = sheet.imports, index = 0, length = imports.length; index < length; index++) {
              sheets.push(imports[index]);
            }
          } catch(e) { }
        }
      }
      // wrap all HTML5 elements with printable elements and add print style sheet
      wrappers = addPrintWrappers(ownerDocument);
      printSheet = addPrintSheet(ownerDocument, cssText.reverse().join(''));
    });

    ownerWindow.attachEvent('onafterprint', cache.onafterprint = function() {
      // remove wrappers, leaving the original elements, and remove print style sheet
      removePrintWrappers(ownerDocument, wrappers);
      destroyElement(printSheet, cache);
    });

    if (typeof namespaces[namespace] == 'undefined') {
      namespaces.add(namespace);
    }
  }

  /**
   * Adds minimal default HTML5 element styles to the given document.
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @param {Object} options Options object.
   */
  function setStyles(ownerDocument, options) {
    // for additional default and normalized HTML5 element styles checkout
    // https://github.com/necolas/normalize.css
    getCache(ownerDocument).sheet = addStyleSheet(ownerDocument,
      // corrects block display not defined in IE6/7/8/9 and Firefox 3
      'article, aside, figcaption, figure, footer, header, main, nav, section {' +
      '  display: block' +
      '}' +
      // adds styling not present in IE6/7/8/9
      'mark {' +
      '  background: #ff0;' +
      '  color: #000' +
      '}'
    );
  }

  /**
   * Restores the document's original `createElement` and `createDocumentFragment` methods.
   *
   * @private
   * @param {Document} ownerDocument The document.
   */
  function unsetMethods(ownerDocument) {
    var cache = getCache(ownerDocument),
        fn = cache.nativeCreateElement;

    if (ownerDocument.createElement != fn) {
      ownerDocument.createElement = fn;
    }
    if (ownerDocument.createDocumentFragment != (fn = cache.nativeCreateFragment)) {
      ownerDocument.createDocumentFragment = fn;
    }
  }

  /**
   * Removes support for printing HTML5 elements.
   *
   * @private
   * @param {Document} ownerDocument The document.
   */
  function unsetPrintSupport(ownerDocument) {
    var cache = getCache(ownerDocument),
        ownerWindow = ownerDocument.parentWindow;

    ownerWindow.detachEvent('onbeforeprint', cache.onbeforeprint || unsetPrintSupport);
    ownerWindow.detachEvent('onafterprint', cache.onafterprint || unsetPrintSupport);
  }

  /**
   * Removes default HTML5 element styles.
   *
   * @private
   * @param {Document} ownerDocument The document.
   * @param {Object} options Options object.
   */
  function unsetStyles(ownerDocument, options) {
    var cache = getCache(ownerDocument),
        sheet = cache.sheet;

    if (sheet) {
      cache.sheet = null;
      destroyElement(sheet, cache);
    }
  }

  /*--------------------------------------------------------------------------*/

  /**
   * Creates a shimmed element of the given node name.
   *
   * @memberOf html5
   * @param {Document} [ownerDocument=document] The context document.
   * @param {String} nodeName The node name of the element to create.
   * @returns {Element} The created element.
   * @example
   *
   * // basic usage
   * html5.createElement('div');
   *
   * // from a child iframe
   * parent.html5.createElement(document, 'div');
   */
  function createElement(ownerDocument, nodeName) {
    // juggle arguments
    ownerDocument || (ownerDocument = document);
    if (ownerDocument && !ownerDocument.nodeType) {
      nodeName = ownerDocument;
      ownerDocument = document;
    }
    if (support.unknownElements) {
      return ownerDocument.createElement(nodeName);
    }
    // Avoid adding some elements to fragments in IE because
    // * attributes like `type` cannot be set/changed once an element is inserted
    //   into a document/fragment
    // * link elements with `src` attributes that are inaccessible, as with
    //   a 403 response, will cause the tab/window to crash
    // * script elements appended to fragments will execute when their `src`
    //   or `text` property is set
    var cache = getCache(ownerDocument),
        nodes = cache.nodes,
        cached = nodes[nodeName],
        node = cached ? cached.cloneNode() : cache.nativeCreateElement(nodeName);

    // IE < 9 doesn't clone unknown elements correctly
    if (!cached && !unclonables[nodeName] &&
        !(unclonables[nodeName] = reUnclonable.test(node.outerHTML))) {
      node = (nodes[nodeName] = node).cloneNode();
    }
    return node.canHaveChildren && !reSkip.test(nodeName) ? cache.frag.appendChild(node) : node;
  }

  /**
   * Creates a shimmed document fragment.
   *
   * @memberOf html5
   * @param {Document} [ownerDocument=document] The context document.
   * @returns {Fragment} The created document fragment.
   * @example
   *
   * // basic usage
   * html5.createDocumentFragment();
   *
   * // from a child iframe
   * parent.html5.createDocumentFragment(document);
   */
  function createDocumentFragment(ownerDocument) {
    ownerDocument || (ownerDocument = document);
    return support.unknownElements
      ? ownerDocument.createDocumentFragment()
      : createElements(getCache(ownerDocument).frag.cloneNode());
  }

  /**
   * Installs shims according to the specified options.
   *
   * @memberOf html5
   * @param {Document} [ownerDocument=document] The document.
   * @param {Object} [options={}] Options object.
   * @returns {Document} The document.
   * @example
   *
   * // basic usage
   * // autmatically called on the primary document to allow IE < 9 to
   * // parse HTML5 elements correctly
   * html5.install();
   *
   * // from a child iframe
   * parent.html5.install(document);
   *
   * // with an options object
   * html5.install({
   *
   *   // overwrite the document's `createElement` and `createDocumentFragment`
   *   // methods with `html5.createElement` and `html5.createDocumentFragment` equivalents.
   *   'methods': true,
   *
   *   // add support for printing HTML5 elements
   *   'print': true,
   *
   *   // add minimal default HTML5 element styles
   *   'styles': true
   * });
   *
   * // with an options string
   * html5.install('print styles');
   *
   * // from a child iframe with options
   * parent.html5.install(document, options);
   *
   * // using a shortcut to install all support extensions
   * html5.install('all');
   */
  function install(ownerDocument, options) {
    ownerDocument || (ownerDocument = document);
    if (ownerDocument && !ownerDocument.nodeType) {
      options = ownerDocument;
      ownerDocument = document;
    }

    options = resolveOptions(options);
    uninstall(ownerDocument, {
      'methods': options.methods,
      'print': options.print,
      'styles': options.styles
    });

    if (!support.html5Styles && options.styles) {
      setStyles(ownerDocument, options);
    }
    if (!support.html5Printing && options.print) {
      setPrintSupport(ownerDocument);
    }
    if (!support.unknownElements) {
      // if not installing methods then init cache and install support
      // for basic HTML5 element parsing
      options.methods ? setMethods(ownerDocument) : getCache(ownerDocument);
    }
    return ownerDocument;
  }

  /**
   * Restores a previously overwritten `html5` object.
   * @memberOf html5
   * @returns {Object} The current `html5` object.
   */
  function noConflict() {
    window.html5 = old;
    return this;
  }

  /**
   * Uninstalls shims according to the specified options.
   *
   * @memberOf html5
   * @param {Document} [ownerDocument=document] The document.
   * @param {Object} [options={}] Options object.
   * @returns {Document} The document.
   * @example
   *
   * // basic usage with an options object
   * html5.uninstall({
   *
   *   // restore the document's original `createElement`
   *   // and `createDocumentFragment` methods.
   *   'methods': true,
   *
   *   // remove support for printing HTML5 elements
   *   'print': true,
   *
   *   // remove minimal default HTML5 element styles
   *   'styles': true
   * });
   *
   * // with an options string
   * html5.uninstall('print styles');
   *
   * // from a child iframe with options
   * parent.html5.uninstall(document, options);
   *
   * // using a shortcut to uninstall all support extensions
   * html5.uninstall('all');
   */
  function uninstall(ownerDocument, options) {
    ownerDocument || (ownerDocument = document);
    if (ownerDocument && !ownerDocument.nodeType) {
      options = ownerDocument;
      ownerDocument = document;
    }
    options = resolveOptions(options);
    if (!support.unknownElements && options.methods) {
      unsetMethods(ownerDocument);
    }
    if (!support.html5Printing && options.print) {
      unsetPrintSupport(ownerDocument);
    }
    if (!support.html5Styles && options.styles) {
      unsetStyles(ownerDocument, options);
    }
    return ownerDocument;
  }

  /*--------------------------------------------------------------------------*/

  /**
   * The `html5` object.
   * @type Object
   */
  var html5 = {

    /**
     * The semantic version number.
     * @static
     * @memberOf html5
     * @type String
     */
    'version': '1.0.0-rc',

    // an object of feature detection flags
    'support': support,

    // creates shimmed document fragments
    'createDocumentFragment': createDocumentFragment,

    // creates shimmed elements
    'createElement': createElement,

    // installs support extensions
    'install': install,

    // avoid `html5` object conflicts
    'noConflict': noConflict,

    // uninstalls support extensions
    'uninstall': uninstall
  };

  /*--------------------------------------------------------------------------*/

  // Expose the `html5` object to the global object even when an AMD loader is
  // present in case html5.js was injected by a third-party script and not
  // intended to be loaded as a module. The global assignment can be reverted in
  // the `html5` module via its `noConflict()` method.
  window.html5 = html5;

  // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
    // define as an anonymous module so, through path mapping, it can be aliased
    define(function() {
      return html5;
    });
  }
}(this, document));