Giter VIP home page Giter VIP logo

inject-data's People

Contributors

arunoda avatar benmgreene avatar tomwasd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

inject-data's Issues

A huge <script type="text/inject-data"> tag is causing twitter card fetching problem

Since I started using react-router-ssr, Twitterbot is returning error "response is too large" when I try to validate my news pages because twitter has a limit: you cannot validate pages with sizes more than 1MB. In my single-news page (http://nurotan.kz/single-news?id=pNJwNp7GYfeSkRCGf&lang=ru) the <script type="text/inject-data"> tag weighs 5MB. Is it possible not to render this tag for certain useragents (twitterbot for instance)?

Doesn't work with Meteor 1.1

.meteor/local/build/programs/server/packages/meteorhacks_inject-data.js:113
W20150401-15:27:19.896(3)? (STDERR)     chunk = chunk.replace('</head>', injectHtml + '\n</head>');           // 4
W20150401-15:27:19.896(3)? (STDERR)                   ^
W20150401-15:27:19.902(3)? (STDERR) TypeError: Object rve.apply(this, wysihtml5.lang.array(arguments).get());\n  },\n\n  fire: function(eventName, payload) {\n    this.events = this.events || {};\n    var handlers = this.events[eventName] || [],\n        i        = 0;\n    for (; i<handlers.length; i++) {\n      handlers[i].call(this, payload);\n    }\n    return this;\n  },\n\n  stopObserving: function(eventName, handler) {\n    this.events = this.events || {};\n    var i = 0,\n        handlers,\n        newHandlers;\n    if (eventName) {\n      handlers    = this.events[eventName] || [],\n      newHandlers = [];\n      for (; i<handlers.length; i++) {\n        if (handlers[i] !== handler && handler) {\n          newHandlers.push(handlers[i]);\n        }\n      }\n      this.events[eventName] = newHandlers;\n    } else {\n      // Clean up all events\n      this.events = {};\n    }\n    return this;\n  }\n});wysihtml5.lang.object = function(obj) {\n  return {\n    /**\n     * @example\n     *    wysihtml5.lang.object({ foo: 1, bar: 1 }).merge({ bar: 2, baz: 3 }).get();\n     *    // => { foo: 1, bar: 2, baz: 3 }\n     */\n    merge: function(otherObj) {\n      for (var i in otherObj) {\n        obj[i] = otherObj[i];\n      }\n      return this;\n    },\n    \n    get: function() {\n      return obj;\n    },\n    \n    /**\n     * @example\n     *    wysihtml5.lang.object({ foo: 1 }).clone();\n     *    // => { foo: 1 }\n     */\n    clone: function() {\n      var newObj = {},\n          i;\n      for (i in obj) {\n        newObj[i] = obj[i];\n      }\n      return newObj;\n    },\n    \n    /**\n     * @example\n     *    wysihtml5.lang.object([]).isArray();\n     *    // => true\n     */\n    isArray: function() {\n      return Object.prototype.toString.call(obj) === \"[object Array]\";\n    }\n  };\n};(function() {\n  var WHITE_SPACE_START = /^\\s+/,\n      WHITE_SPACE_END   = /\\s+$/;\n  wysihtml5.lang.string = function(str) {\n    str = String(str);\n    return {\n      /**\n       * @example\n       *    wysihtml5.lang.string(\"   foo   \").trim();\n       *    // => \"foo\"\n       */\n      trim: function() {\n        return str.replace(WHITE_SPACE_START, \"\").replace(WHITE_SPACE_END, \"\");\n      },\n      \n      /**\n       * @example\n       *    wysihtml5.lang.string(\"Hello #{name}\").interpolate({ name: \"Christopher\" });\n       *    // => \"Hello Christopher\"\n       */\n      interpolate: function(vars) {\n        for (var i in vars) {\n          str = this.replace(\"#{\" + i + \"}\").by(vars[i]);\n        }\n        return str;\n      },\n      \n      /**\n       * @example\n       *    wysihtml5.lang.string(\"Hello Tom\").replace(\"Tom\").with(\"Hans\");\n       *    // => \"Hello Hans\"\n       */\n      replace: function(search) {\n        return {\n          by: function(replace) {\n            return str.split(search).join(replace);\n          }\n        }\n      }\n    };\n  };\n})();/**\n * Find urls in descendant text nodes of an element and auto-links them\n * Inspired by http://james.padolsey.com/javascript/find-and-replace-text-with-javascript/\n *\n * @param {Element} element Container element in which to search for urls\n *\n * @example\n *    <div id=\"text-container\">Please click here: www.google.com</div>\n *    <script>wysihtml5.dom.autoLink(document.getElementById(\"text-container\"));</script>\n */\n(function(wysihtml5) {\n  var /**\n       * Don't auto-link urls that are contained in the following elements:\n       */\n      IGNORE_URLS_IN        = wysihtml5.lang.array([\"CODE\", \"PRE\", \"A\", \"SCRIPT\", \"HEAD\", \"TITLE\", \"STYLE\"]),\n      /**\n       * revision 1:\n       *    /(\\S+\\.{1}[^\\s\\,\\.\\!]+)/g\n       *\n       * revision 2:\n       *    /(\\b(((https?|ftp):\\/\\/)|(www\\.))[-A-Z0-9+&@#\\/%?=~_|!:,.;\\[\\]]*[-A-Z0-9+&@#\\/%=~_|])/gim\n       *\n       * put this in the beginning if you don't wan't to match within a word\n       *    (^|[\\>\\(\\{\\[\\s\\>])\n       */\n      URL_REG_EXP           = /((https?:\\/\\/|www\\.)[^\\s<]{3,})/gi,\n      TRAILING_CHAR_REG_EXP = /([^\\w\\/\\-](,?))$/i,\n      MAX_DISPLAY_LENGTH    = 100,\n      BRACKETS              = { \")\": \"(\", \"]\": \"[\", \"}\": \"{\" };\n  \n  function autoLink(element) {\n    if (_hasParentThatShouldBeIgnored(element)) {\n      return element;\n    }\n\n    if (element === element.ownerDocument.documentElement) {\n      element = element.ownerDocument.body;\n    }\n\n    return _parseNode(element);\n  }\n  \n  /**\n   * This is basically a rebuild of\n   * the rails auto_link_urls text helper\n   */\n  function _convertUrlsToLinks(str) {\n    return str.replace(URL_REG_EXP, function(match, url) {\n      var punctuation = (url.match(TRAILING_CHAR_REG_EXP) || [])[1] || \"\",\n          opening     = BRACKETS[punctuation];\n      url = url.replace(TRAILING_CHAR_REG_EXP, \"\");\n\n      if (url.split(opening).length > url.split(punctuation).length) {\n        url = url + punctuation;\n        punctuation = \"\";\n      }\n      var realUrl    = url,\n          displayUrl = url;\n      if (url.length > MAX_DISPLAY_LENGTH) {\n        displayUrl = displayUrl.substr(0, MAX_DISPLAY_LENGTH) + \"...\";\n      }\n      // Add http prefix if necessary\n      if (realUrl.substr(0, 4) === \"www.\") {\n        realUrl = \"http://\" + realUrl;\n      }\n      \n      return '<a href=\"' + realUrl + '\">' + displayUrl + '</a>' + punctuation;\n    });\n  }\n  \n  /**\n   * Creates or (if already cached) returns a temp element\n   * for the given document object\n   */\n  function _getTempElement(context) {\n    var tempElement = context._wysihtml5_tempElement;\n    if (!tempElement) {\n      tempElement = context._wysihtml5_tempElement = context.createElement(\"div\");\n    }\n    return tempElement;\n  }\n  \n  /**\n   * Replaces the original text nodes with the newly auto-linked dom tree\n   */\n  function _wrapMatchesInNode(textNode) {\n    var parentNode  = textNode.parentNode,\n        tempElement = _getTempElement(parentNode.ownerDocument);\n    \n    // We need to insert an empty/temporary <span /> to fix IE quirks\n    // Elsewise IE would strip white space in the beginning\n    tempElement.innerHTML = \"<span></span>\" + _convertUrlsToLinks(textNode.data);\n    tempElement.removeChild(tempElement.firstChild);\n    \n    while (tempElement.firstChild) {\n      // inserts tempElement.firstChild before textNode\n      parentNode.insertBefore(tempElement.firstChild, textNode);\n    }\n    parentNode.removeChild(textNode);\n  }\n  \n  function _hasParentThatShouldBeIgnored(node) {\n    var nodeName;\n    while (node.parentNode) {\n      node = node.parentNode;\n      nodeName = node.nodeName;\n      if (IGNORE_URLS_IN.contains(nodeName)) {\n        return true;\n      } else if (nodeName === \"body\") {\n        return false;\n      }\n    }\n    return false;\n  }\n  \n  function _parseNode(element) {\n    if (IGNORE_URLS_IN.contains(element.nodeName)) {\n      return;\n    }\n    \n    if (element.nodeType === wysihtml5.TEXT_NODE && element.data.match(URL_REG_EXP)) {\n      _wrapMatchesInNode(element);\n      return;\n    }\n    \n    var childNodes        = wysihtml5.lang.array(element.childNodes).get(),\n        childNodesLength  = childNodes.length,\n        i                 = 0;\n    \n    for (; i<childNodesLength; i++) {\n      _parseNode(childNodes[i]);\n    }\n    \n    return element;\n  }\n  \n  wysihtml5.dom.autoLink = autoLink;\n  \n  // Reveal url reg exp to the outside\n  wysihtml5.dom.autoLink.URL_REG_EXP = URL_REG_EXP;\n})(wysihtml5);(function(wysihtml5) {\n  var supportsClassList = wysihtml5.browser.supportsClassList(),\n      api               = wysihtml5.dom;\n  \n  api.addClass = function(element, className) {\n    if (supportsClassList) {\n      return element.classList.add(className);\n    }\n    if (api.hasClass(element, className)) {\n      return;\n    }\n    element.className += \" \" + className;\n  };\n  \n  api.removeClass = function(element, className) {\n    if (supportsClassList) {\n      return element.classList.remove(className);\n    }\n    \n    element.className = element.className.replace(new RegExp(\"(^|\\\\s+)\" + className + \"(\\\\s+|$)\"), \" \");\n  };\n  \n  api.hasClass = function(element, className) {\n    if (supportsClassList) {\n      return element.classList.contains(className);\n    }\n    \n    var elementClassName = element.className;\n    return (elementClassName.length > 0 && (elementClassName == className || new RegExp(\"(^|\\\\s)\" + className + \"(\\\\s|$)\").test(elementClassName)));\n  };\n})(wysihtml5);\nwysihtml5.dom.contains = (function() {\n  var documentElement = document.documentElement;\n  if (documentElement.contains) {\n    return function(container, element) {\n      if (element.nodeType !== wysihtml5.ELEMENT_NODE) {\n        element = element.parentNode;\n      }\n      return container !== element && container.contains(element);\n    };\n  } else if (documentElement.compareDocumentPosition) {\n    return function(container, element) {\n      // https://developer.mozilla.org/en/DOM/Node.compareDocumentPosition\n      return !!(container.compareDocumentPosition(element) & 16);\n    };\n  }\n})();/**\n * Converts an HTML fragment/element into a unordered/ordered list\n *\n * @param {Element} element The element which should be turned into a list\n * @param {String} listType The list type in which to convert the tree (either \"ul\" or \"ol\")\n * @return {Element} The created list\n *\n * @example\n *    <!-- Assume the following dom: -->\n *    <span id=\"pseudo-list\">\n *      eminem<br>\n *      dr. dre\n *      <div>50 Cent</div>\n *    </span>\n *\n *    <script>\n *      wysihtml5.dom.convertToList(document.getElementById(\"pseudo-list\"), \"ul\");\n *    </script>\n *\n *    <!-- Will result in: -->\n *    <ul>\n *      <li>eminem</li>\n *      <li>dr. dre</li>\n *      <li>50 Cent</li>\n *    </ul>\n */\nwysihtml5.dom.convertToList = (function() {\n  function _createListItem(doc, list) {\n    var listItem = doc.createElement(\"li\");\n    list.appendChild(listItem);\n    return listItem;\n  }\n  \n  function _createList(doc, type) {\n    return doc.createElement(type);\n  }\n  \n  function convertToList(element, listType) {\n    if (element.nodeName === \"UL\" || element.nodeName === \"OL\" || element.nodeName === \"MENU\") {\n      // Already a list\n      return element;\n    }\n    \n    var doc               = element.ownerDocument,\n        list              = _createList(doc, listType),\n        lineBreaks        = element.querySelectorAll(\"br\"),\n        lineBreaksLength  = lineBreaks.length,\n        childNodes,\n        childNodesLength,\n        childNode,\n        lineBreak,\n        parentNode,\n        isBlockElement,\n        isLineBreak,\n        currentListItem,\n        i;\n    \n    // First find <br> at the end of inline elements and move them behind them\n    for (i=0; i<lineBreaksLength; i++) {\n      lineBreak = lineBreaks[i];\n      while ((parentNode = lineBreak.parentNode) && parentNode !== element && parentNode.lastChild === lineBreak) {\n        if (wysihtml5.dom.getStyle(\"display\").from(parentNode) === \"block\") {\n          parentNode.removeChild(lineBreak);\n          break;\n        }\n        wysihtml5.dom.insert(lineBreak).after(lineBreak.parentNode);\n      }\n    }\n    \n    childNodes        = wysihtml5.lang.array(element.childNodes).get();\n    childNodesLength  = childNodes.length;\n    \n    for (i=0; i<childNodesLength; i++) {\n      currentListItem   = currentListItem || _createListItem(doc, list);\n      childNode         = childNodes[i];\n      isBlockElement    = wysihtml5.dom.getStyle(\"display\").from(childNode) === \"block\";\n      isLineBreak       = childNode.nodeName === \"BR\";\n      \n      if (isBlockElement) {\n        // Append blockElement to current <li> if empty, otherwise create a new one\n        currentListItem = currentListItem.firstChild ? _createListItem(doc, list) : currentListItem;\n        currentListItem.appendChild(childNode);\n        currentListItem = null;\n        continue;\n      }\n      \n      if (isLineBreak) {\n        // Only create a new list item in the next iteration when the current one has already content\n        currentListItem = currentListItem.firstChild ? null : currentListItem;\n        continue;\n      }\n      \n      currentListItem.appendChild(childNode);\n    }\n    \n    element.parentNode.replaceChild(list, element);\n    return list;\n  }\n  \n  return convertToList;\n})();/**\n * Copy a set of attributes from one element to another\n *\n * @param {Array} attributesToCopy List of attributes which should be copied\n * @return {Object} Returns an object which offers the \"from\" method which can be invoked with the element where to\n *    copy the attributes from., this again returns an object which provides a method named \"to\" which can be invoked \n *    with the element where to copy the attributes to (see example)\n *\n * @example\n *    var textarea    = document.querySelector(\"textarea\"),\n *        div         = document.querySelector(\"div[contenteditable=true]\"),\n *        anotherDiv  = document.querySelector(\"div.preview\");\n *    wysihtml5.dom.copyAttributes([\"spellcheck\", \"value\", \"placeholder\"]).from(textarea).to(div).andTo(anotherDiv);\n *\n */\nwysihtml5.dom.copyAttributes = function(attributesToCopy) {\n  return {\n    from: function(elementToCopyFrom) {\n      return {\n        to: function(elementToCopyTo) {\n          var attribute,\n              i         = 0,\n              length    = attributesToCopy.length;\n          for (; i<length; i++) {\n            attribute = attributesToCopy[i];\n            if (typeof(elementToCopyFrom[attribute]) !== \"undefined\" && elementToCopyFrom[attribute] !== \"\") {\n              elementToCopyTo[attribute] = elementToCopyFrom[attribute];\n            }\n          }\n          return { andTo: arguments.callee };\n        }\n      };\n    }\n  };\n};/**\n * Copy a set of styles from one element to another\n * Please note that this only works properly across browsers when the element from which to copy the styles\n * is in the dom\n *\n * Interesting article on how to copy styles\n *\n * @param {Array} stylesToCopy List of styles which should be copied\n * @return {Object} Returns an object which offers the \"from\" method which can be invoked with the element where to\n *    copy the styles from., this again returns an object which provides a method named \"to\" which can be invoked \n *    with the element where to copy the styles to (see example)\n *\n * @example\n *    var textarea    = document.querySelector(\"textarea\"),\n *        div         = document.querySelector(\"div[contenteditable=true]\"),\n *        anotherDiv  = document.querySelector(\"div.preview\");\n *    wysihtml5.dom.copyStyles([\"overflow-y\", \"width\", \"height\"]).from(textarea).to(div).andTo(anotherDiv);\n *\n */\n(function(dom) {\n  \n  /**\n   * Mozilla, WebKit and Opera recalculate the computed width when box-sizing: boder-box; is set\n   * So if an element has \"width: 200px; -moz-box-sizing: border-box; border: 1px;\" then \n   * its computed css width will be 198px\n   */\n  var BOX_SIZING_PROPERTIES = [\"-webkit-box-sizing\", \"-moz-box-sizing\", \"-ms-box-sizing\", \"box-sizing\"];\n  \n  var shouldIgnoreBoxSizingBorderBox = function(element) {\n    if (hasBoxSizingBorderBox(element)) {\n       return parseInt(dom.getStyle(\"width\").from(element), 10) < element.offsetWidth;\n    }\n    return false;\n  };\n  \n  var hasBoxSizingBorderBox = function(element) {\n    var i       = 0,\n        length  = BOX_SIZING_PROPERTIES.length;\n    for (; i<length; i++) {\n      if (dom.getStyle(BOX_SIZING_PROPERTIES[i]).from(element) === \"border-box\") {\n        return BOX_SIZING_PROPERTIES[i];\n      }\n    }\n  };\n  \n  dom.copyStyles = function(stylesToCopy) {\n    return {\n      from: function(element) {\n        if (shouldIgnoreBoxSizingBorderBox(element)) {\n          stylesToCopy = wysihtml5.lang.array(stylesToCopy).without(BOX_SIZING_PROPERTIES);\n        }\n        \n        var cssText = \"\",\n            length  = stylesToCopy.length,\n            i       = 0,\n            property;\n        for (; i<length; i++) {\n          property = stylesToCopy[i];\n          cssText += property + \":\" + dom.getStyle(property).from(element) + \";\";\n        }\n        \n        return {\n          to: function(element) {\n            dom.setStyles(cssText).on(element);\n            return { andTo: arguments.callee };\n          }\n        };\n      }\n    };\n  };\n})(wysihtml5.dom);/**\n * Event Delegation\n *\n * @example\n *    wysihtml5.dom.delegate(document.body, \"a\", \"click\", function() {\n *      // foo\n *    });\n */\n(function(wysihtml5) {\n  \n  wysihtml5.dom.delegate = function(container, selector, eventName, handler) {\n    return wysihtml5.dom.observe(container, eventName, function(event) {\n      var target    = event.target,\n          match     = wysihtml5.lang.array(container.querySelectorAll(selector));\n      \n      while (target && target !== container) {\n        if (match.contains(target)) {\n          handler.call(target, event);\n          break;\n        }\n        target = target.parentNode;\n      }\n    });\n  };\n  \n})(wysihtml5);/**\n * Returns the given html wrapped in a div element\n *\n * Fixing IE's inability to treat unknown elements (HTML5 section, article, ...) correctly\n * when inserted via innerHTML\n * \n * @param {String} html The html which should be wrapped in a dom element\n * @param {Obejct} [context] Document object of the context the html belongs to\n *\n * @example\n *    wysihtml5.dom.getAsDom(\"<article>foo</article>\");\n */\nwysihtml5.dom.getAsDom = (function() {\n  \n  var _innerHTMLShiv = function(html, context) {\n    var tempElement = context.createElement(\"div\");\n    tempElement.style.display = \"none\";\n    context.body.appendChild(tempElement);\n    // IE throws an exception when trying to insert <frameset></frameset> via innerHTML\n    try { tempElement.innerHTML = html; } catch(e) {}\n    context.body.removeChild(tempElement);\n    return tempElement;\n  };\n  \n  /**\n   * Make sure IE supports HTML5 tags, which is accomplished by simply creating one instance of each element\n   */\n  var _ensureHTML5Compatibility = function(context) {\n    if (context._wysihtml5_supportsHTML5Tags) {\n      return;\n    }\n    for (var i=0, length=HTML5_ELEMENTS.length; i<length; i++) {\n      context.createElement(HTML5_ELEMENTS[i]);\n    }\n    context._wysihtml5_supportsHTML5Tags = true;\n  };\n  \n  \n  /**\n   * List of html5 tags\n   * taken from http://simon.html5.org/html5-elements\n   */\n  var HTML5_ELEMENTS = [\n    \"abbr\", \"article\", \"aside\", \"audio\", \"bdi\", \"canvas\", \"command\", \"datalist\", \"details\", \"figcaption\",\n    \"figure\", \"footer\", \"header\", \"hgroup\", \"keygen\", \"mark\", \"meter\", \"nav\", \"output\", \"progress\",\n    \"rp\", \"rt\", \"ruby\", \"svg\", \"section\", \"source\", \"summary\", \"time\", \"track\", \"video\", \"wbr\"\n  ];\n  \n  return function(html, context) {\n    context = context || document;\n    var tempElement;\n    if (typeof(html) === \"object\" && html.nodeType) {\n      tempElement = context.createElement(\"div\");\n      tempElement.appendChild(html);\n    } else if (wysihtml5.browser.supportsHTML5Tags(context)) {\n      tempElement = context.createElement(\"div\");\n      tempElement.innerHTML = html;\n    } else {\n      _ensureHTML5Compatibility(context);\n      tempElement = _innerHTMLShiv(html, context);\n    }\n    return tempElement;\n  };\n})();/**\n * Walks the dom tree from the given node up until it finds a match\n * Designed for optimal performance.\n *\n * @param {Element} node The from which to check the parent nodes\n * @param {Object} matchingSet Object to match against (possible properties: nodeName, className, classRegExp)\n * @param {Number} [levels] How many parents should the function check up from the current node (defaults to 50)\n * @return {null|Element} Returns the first element that matched the desiredNodeName(s)\n * @example\n *    var listElement = wysihtml5.dom.getParentElement(document.querySelector(\"li\"), { nodeName: [\"MENU\", \"UL\", \"OL\"] });\n *    // ... or ...\n *    var unorderedListElement = wysihtml5.dom.getParentElement(document.querySelector(\"li\"), { nodeName: \"UL\" });\n *    // ... or ...\n *    var coloredElement = wysihtml5.dom.getParentElement(myTextNode, { nodeName: \"SPAN\", className: \"wysiwyg-color-red\", classRegExp: /wysiwyg-color-[a-z]/g });\n */\nwysihtml5.dom.getParentElement = (function() {\n  \n  function _isSameNodeName(nodeName, desiredNodeNames) {\n    if (!desiredNodeNames || !desiredNodeNames.length) {\n      return true;\n    }\n    \n    if (typeof(desiredNodeNames) === \"string\") {\n      return nodeName === desiredNodeNames;\n    } else {\n      return wysihtml5.lang.array(desiredNodeNames).contains(nodeName);\n    }\n  }\n  \n  function _isElement(node) {\n    return node.nodeType === wysihtml5.ELEMENT_NODE;\n  }\n  \n  function _hasClassName(element, className, classRegExp) {\n    var classNames = (element.className || \"\").match(classRegExp) || [];\n    if (!className) {\n      return !!classNames.length;\n    }\n    return classNames[classNames.length - 1] === className;\n  }\n  \n  function _getParentElementWithNodeName(node, nodeName, levels) {\n    while (levels-- && node && node.nodeName !== \"BODY\") {\n      if (_isSameNodeName(node.nodeName, nodeName)) {\n        return node;\n      }\n      node = node.parentNode;\n    }\n    return null;\n  }\n  \n  function _getParentElementWithNodeNameAndClassName(node, nodeName, className, classRegExp, levels) {\n    while (levels-- && node && node.nodeName !== \"BODY\") {\n      if (_isElement(node) &&\n          _isSameNodeName(node.nodeName, nodeName) &&\n          _hasClassName(node, className, classRegExp)) {\n        return node;\n      }\n      node = node.parentNode;\n    }\n    return null;\n  }\n  \n  return function(node, matchingSet, levels) {\n    levels = levels || 50; // Go max 50 nodes upwards from current node\n    if (matchingSet.className || matchingSet.classRegExp) {\n      return _getParentElementWithNodeNameAndClassName(\n        node, matchingSet.nodeName, matchingSet.className, matchingSet.classRegExp, levels\n      );\n    } else {\n      return _getParentElementWithNodeName(\n        node, matchingSet.nodeName, levels\n      );\n    }\n  };\n})();\n/**\n * Get element's style for a specific css property\n *\n * @param {Element} element The element on which to retrieve the style\n * @param {String} property The CSS property to retrieve (\"float\", \"display\", \"text-align\", ...)\n *\n * @example\n *    wysihtml5.dom.getStyle(\"display\").from(document.body);\n *    // => \"block\"\n */\nwysihtml5.dom.getStyle = (function() {\n  var stylePropertyMapping = {\n        \"float\": (\"styleFloat\" in document.createElement(\"div\").style) ? \"styleFloat\" : \"cssFloat\"\n      },\n      REG_EXP_CAMELIZE = /\\-[a-z]/g;\n  \n  function camelize(str) {\n    return str.replace(REG_EXP_CAMELIZE, function(match) {\n      return match.charAt(1).toUpperCase();\n    });\n  }\n  \n  return function(property) {\n    return {\n      from: function(element) {\n        if (element.nodeType !== wysihtml5.ELEMENT_NODE) {\n          return;\n        }\n        \n        var doc               = element.ownerDocument,\n            camelizedProperty = stylePropertyMapping[property] || camelize(property),\n            style             = element.style,\n            currentStyle      = element.currentStyle,\n            styleValue        = style[camelizedProperty];\n        if (styleValue) {\n          return styleValue;\n        }\n        \n        // currentStyle is no standard and only supported by Opera and IE but it has one important advantage over the standard-compliant\n        // window.getComputedStyle, since it returns css property values in their original unit:\n        // If you set an elements width to \"50%\", window.getComputedStyle will give you it's current width in px while currentStyle\n        // gives you the original \"50%\".\n        // Opera supports both, currentStyle and window.getComputedStyle, that's why checking for currentStyle should have higher prio\n        if (currentStyle) {\n          try {\n                return currentStyle[camelizedProperty];\n          } catch(e) {\n            //ie will occasionally fail for unknown reasons. swallowing exception\n          }\n        }\n\n        var win                 = doc.defaultView || doc.parentWindow,\n            needsOverflowReset  = (property === \"height\" || property === \"width\") && element.nodeName === \"TEXTAREA\",\n            originalOverflow,\n            returnValue;\n\n        if (win.getComputedStyle) {\n          // Chrome and Safari both calculate a wrong width and height for textareas when they have scroll bars\n          // therfore we remove and restore the scrollbar and calculate the value in between\n          if (needsOverflowReset) {\n            originalOverflow = style.overflow;\n            style.overflow = \"hidden\";\n          }\n          returnValue = win.getComputedStyle(element, null).getPropertyValue(property);\n          if (needsOverflowReset) {\n            style.overflow = originalOverflow || \"\";\n          }\n          return returnValue;\n        }\n      }\n    };\n  };\n})();/**\n * High performant way to check whether an element with a specific tag name is in the given document\n * Optimized for being heavily executed\n * Unleashes the power of live node lists\n *\n * @param {Object} doc The document object of the context where to check\n * @param {String} tagName Upper cased tag name\n * @example\n *    wysihtml5.dom.hasElementWithTagName(document, \"IMG\");\n */\nwysihtml5.dom.hasElementWithTagName = (function() {\n  var LIVE_CACHE          = {},\n      DOCUMENT_IDENTIFIER = 1;\n  \n  function _getDocumentIdentifier(doc) {\n    return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);\n  }\n  \n  return function(doc, tagName) {\n    var key         = _getDocumentIdentifier(doc) + \":\" + tagName,\n        cacheEntry  = LIVE_CACHE[key];\n    if (!cacheEntry) {\n      cacheEntry = LIVE_CACHE[key] = doc.getElementsByTagName(tagName);\n    }\n    \n    return cacheEntry.length > 0;\n  };\n})();/**\n * High performant way to check whether an element with a specific class name is in the given document\n * Optimized for being heavily executed\n * Unleashes the power of live node lists\n *\n * @param {Object} doc The document object of the context where to check\n * @param {String} tagName Upper cased tag name\n * @example\n *    wysihtml5.dom.hasElementWithClassName(document, \"foobar\");\n */\n(function(wysihtml5) {\n  var LIVE_CACHE          = {},\n      DOCUMENT_IDENTIFIER = 1;\n\n  function _getDocumentIdentifier(doc) {\n    return doc._wysihtml5_identifier || (doc._wysihtml5_identifier = DOCUMENT_IDENTIFIER++);\n  }\n  \n  wysihtml5.dom.hasElementWithClassName = function(doc, className) {\n    // getElementsByClassName is not supported by IE<9\n    // but is sometimes mocked via library code (which then doesn't return live node lists)\n    if (!wysihtml5.browser.supportsNativeGetElementsByClassName()) {\n      return !!doc.querySelector(\".\" + className);\n    }\n\n    var key         = _getDocumentIdentifier(doc) + \":\" + className,\n        cacheEntry  = LIVE_CACHE[key];\n    if (!cacheEntry) {\n      cacheEntry = LIVE_CACHE[key] = doc.getElementsByClassName(className);\n    }\n\n    return cacheEntry.length > 0;\n  };\n})(wysihtml5);\nwysihtml5.dom.insert = function(elementToInsert) {\n  return {\n    after: function(element) {\n      element.parentNode.insertBefore(elementToInsert, element.nextSibling);\n    },\n    \n    before: function(element) {\n      element.parentNode.insertBefore(elementToInsert, element);\n    },\n    \n    into: function(element) {\n      element.appendChild(elementToInsert);\n    }\n  };\n};wysihtml5.dom.insertCSS = function(rules) {\n  rules = rules.join(\"\\n\");\n  \n  return {\n    into: function(doc) {\n      var head         = doc.head || doc.getElementsByTagName(\"head\")[0],\n          styleElement = doc.createElement(\"style\");\n\n      styleElement.type = \"text/css\";\n\n      if (styleElement.styleSheet) {\n        styleElement.styleSheet.cssText = rules;\n      } else {\n        styleElement.appendChild(doc.createTextNode(rules));\n      }\n\n      if (head) {\n        head.appendChild(styleElement);\n      }\n    }\n  };\n};/**\n * Method to set dom events\n *\n * @example\n *    wysihtml5.dom.observe(iframe.contentWindow.document.body, [\"focus\", \"blur\"], function() { ... });\n */\nwysihtml5.dom.observe = function(element, eventNames, handler) {\n  eventNames = typeof(eventNames) === \"string\" ? [eventNames] : eventNames;\n  \n  var handlerWrapper,\n      eventName,\n      i       = 0,\n      length  = eventNames.length;\n  \n  for (; i<length; i++) {\n    eventName = eventNames[i];\n    if (element.addEventListener) {\n      element.addEventListener(eventName, handler, false);\n    } else {\n      handlerWrapper = function(event) {\n        if (!(\"target\" in event)) {\n          event.target = event.srcElement;\n        }\n        event.preventDefault = event.preventDefault || function() {\n          this.returnValue = false;\n        };\n        event.stopPropagation = event.stopPropagation || function() {\n          this.cancelBubble = true;\n        };\n        handler.call(element, event);\n      };\n      element.attachEvent(\"on\" + eventName, handlerWrapper);\n    }\n  }\n  \n  return {\n    stop: function() {\n      var eventName,\n          i       = 0,\n          length  = eventNames.length;\n      for (; i<length; i++) {\n        eventName = eventNames[i];\n        if (element.removeEventListener) {\n          element.removeEventListener(eventName, handler, false);\n        } else {\n          element.detachEvent(\"on\" + eventName, handlerWrapper);\n        }\n      }\n    }\n  };\n};\n/**\n * HTML Sanitizer\n * Rewrites the HTML based on given rules\n *\n * @param {Element|String} elementOrHtml HTML String to be sanitized OR element whose content should be sanitized\n * @param {Object} [rules] List of rules for rewriting the HTML, if there's no rule for an element it will\n *    be converted to a \"span\". Each rule is a key/value pair where key is the tag to convert, and value the\n *    desired substitution.\n * @param {Object} context Document object in which to parse the html, needed to sandbox the parsing\n *\n * @return {Element|String} Depends on the elementOrHtml parameter. When html then the sanitized html as string elsewise the element.\n *\n * @example\n *    var userHTML = '<div id=\"foo\" onclick=\"alert(1);\"><p><font color=\"red\">foo</font><script>alert(1);</script></p></div>';\n *    wysihtml5.dom.parse(userHTML, {\n *      tags {\n *        p:      \"div\",      // Rename p tags to div tags\n *        font:   \"span\"      // Rename font tags to span tags\n *        div:    true,       // Keep them, also possible (same result when passing: \"div\" or true)\n *        script: undefined   // Remove script elements\n *      }\n *    });\n *    // => <div><div><span>foo bar</span></div></div>\n *\n *    var userHTML = '<table><tbody><tr><td>I'm a table!</td></tr></tbody></table>';\n *    wysihtml5.dom.parse(userHTML);\n *    // => '<span><span><span><span>I'm a table!</span></span></span></span>'\n *\n *    var userHTML = '<div>foobar<br>foobar</div>';\n *    wysihtml5.dom.parse(userHTML, {\n *      tags: {\n *        div: undefined,\n *        br:  true\n *      }\n *    });\n *    // => ''\n *\n *    var userHTML = '<div class=\"red\">foo</div><div class=\"pink\">bar</div>';\n *    wysihtml5.dom.parse(userHTML, {\n *      classes: {\n *        red:    1,\n *        green:  1\n *      },\n *      tags: {\n *        div: {\n *          rename_tag:     \"p\"\n *        }\n *      }\n *    });\n *    // => '<p class=\"red\">foo</p><p>bar</p>'\n */\nwysihtml5.dom.parse = (function() {\n  \n  /**\n   * It's not possible to use a XMLParser/DOMParser as HTML5 is not always well-formed XML\n   * new DOMParser().parseFromString('<img src=\"foo.gif\">') will cause a parseError since the\n   * node isn't closed\n   *\n   * Therefore we've to use the browser's ordinary HTML parser invoked by setting innerHTML.\n   */\n  var NODE_TYPE_MAPPING = {\n        \"1\": _handleElement,\n        \"3\": _handleText\n      },\n      // Rename unknown tags to this\n      DEFAULT_NODE_NAME   = \"span\",\n      WHITE_SPACE_REG_EXP = /\\s+/,\n      defaultRules        = { tags: {}, classes: {} },\n      currentRules        = {};\n  \n  /**\n   * Iterates over all childs of the element, recreates them, appends them into a document fragment\n   * which later replaces the entire body content\n   */\n  function parse(elementOrHtml, rules, context, cleanUp) {\n    wysihtml5.lang.object(currentRules).merge(defaultRules).merge(rules).get();\n    \n    context           = context || elementOrHtml.ownerDocument || document;\n    var fragment      = context.createDocumentFragment(),\n        isString      = typeof(elementOrHtml) === \"string\",\n        element,\n        newNode,\n        firstChild;\n    \n    if (isString) {\n      element = wysihtml5.dom.getAsDom(elementOrHtml, context);\n    } else {\n      element = elementOrHtml;\n    }\n    \n    while (element.firstChild) {\n      firstChild  = element.firstChild;\n      element.removeChild(firstChild);\n      newNode = _convert(firstChild, cleanUp);\n      if (newNode) {\n        fragment.appendChild(newNode);\n      }\n    }\n    \n    // Clear element contents\n    element.innerHTML = \"\";\n    \n    // Insert new DOM tree\n    element.appendChild(fragment);\n    \n    return isString ? wysihtml5.quirks.getCorrectInnerHTML(element) : element;\n  }\n  \n  function _convert(oldNode, cleanUp) {\n    var oldNodeType     = oldNode.nodeType,\n        oldChilds       = oldNode.childNodes,\n        oldChildsLength = oldChilds.length,\n        newNode,\n        method          = NODE_TYPE_MAPPING[oldNodeType],\n        i               = 0;\n    \n    newNode = method && method(oldNode);\n    \n    if (!newNode) {\n      return null;\n    }\n    \n    for (i=0; i<oldChildsLength; i++) {\n      newChild = _convert(oldChilds[i], cleanUp);\n      if (newChild) {\n        newNode.appendChild(newChild);\n      }\n    }\n    \n    // Cleanup senseless <span> elements\n    if (cleanUp &&\n        newNode.childNodes.length <= 1 &&\n        newNode.nodeName.toLowerCase() === DEFAULT_NODE_NAME &&\n        !newNode.attributes.length) {\n      return newNode.firstChild;\n    }\n    \n    return newNode;\n  }\n  \n  function _handleElement(oldNode) {\n    var rule,\n        newNode,\n        endTag,\n        tagRules    = currentRules.tags,\n        nodeName    = oldNode.nodeName.toLowerCase(),\n        scopeName   = oldNode.scopeName;\n    \n    /**\n     * We already parsed that element\n     * ignore it! (yes, this sometimes happens in IE8 when the html is invalid)\n     */\n    if (oldNode._wysihtml5) {\n      return null;\n    }\n    oldNode._wysihtml5 = 1;\n    \n    if (oldNode.className === \"wysihtml5-temp\") {\n      return null;\n    }\n    \n    /**\n     * IE is the only browser who doesn't include the namespace in the\n     * nodeName, that's why we have to prepend it by ourselves\n     * scopeName is a proprietary IE feature\n     * read more here http://msdn.microsoft.com/en-us/library/ms534388(v=vs.85).aspx\n     */\n    if (scopeName && scopeName != \"HTML\") {\n      nodeName = scopeName + \":\" + nodeName;\n    }\n    \n    /**\n     * Repair node\n     * IE is a bit bitchy when it comes to invalid nested markup which includes unclosed tags\n     * A <p> doesn't need to be closed according HTML4-5 spec, we simply replace it with a <div> to preserve its content and layout\n     */\n    if (\"outerHTML\" in oldNode) {\n      if (!wysihtml5.browser.autoClosesUnclosedTags() &&\n          oldNode.nodeName === \"P\" &&\n          oldNode.outerHTML.slice(-4).toLowerCase() !== \"</p>\") {\n        nodeName = \"div\";\n      }\n    }\n    \n    if (nodeName in tagRules) {\n      rule = tagRules[nodeName];\n      if (!rule || rule.remove) {\n        return null;\n      }\n      \n      rule = typeof(rule) === \"string\" ? { rename_tag: rule } : rule;\n    } else if (oldNode.firstChild) {\n      rule = { rename_tag: DEFAULT_NODE_NAME };\n    } else {\n      // Remove empty unknown elements\n      return null;\n    }\n    \n    newNode = oldNode.ownerDocument.createElement(rule.rename_tag || nodeName);\n    _handleAttributes(oldNode, newNode, rule);\n    \n    oldNode = null;\n    return newNode;\n  }\n  \n  function _handleAttributes(oldNode, newNode, rule) {\n    var attributes          = {},                         // fresh new set of attributes to set on newNode\n        setClass            = rule.set_class,             // classes to set\n        addClass            = rule.add_class,             // add classes based on existing attributes\n        setAttributes       = rule.set_attributes,        // attributes to set on the current node\n        checkAttributes     = rule.check_attributes,      // check/convert values of attributes\n        allowedClasses      = currentRules.classes,\n        i                   = 0,\n        classes             = [],\n        newClasses          = [],\n        newUniqueClasses    = [],\n        oldClasses          = [],\n        classesLength,\n        newClassesLength,\n        currentClass,\n        newClass,\n        attributeName,\n        newAttributeValue,\n        method;\n    \n    if (setAttributes) {\n      attributes = wysihtml5.lang.object(setAttributes).clone();\n    }\n    \n    if (checkAttributes) {\n      for (attributeName in checkAttributes) {\n        method = attributeCheckMethods[checkAttributes[attributeName]];\n        if (!method) {\n          continue;\n        }\n        newAttributeValue = method(_getAttribute(oldNode, attributeName));\n        if (typeof(newAttributeValue) === \"string\") {\n          attributes[attributeName] = newAttributeValue;\n        }\n      }\n    }\n    \n    if (setClass) {\n      classes.push(setClass);\n    }\n    \n    if (addClass) {\n      for (attributeName in addClass) {\n        method = addClassMethods[addClass[attributeName]];\n        if (!method) {\n          continue;\n        }\n        newClass = method(_getAttribute(oldNode, attributeName));\n        if (typeof(newClass) === \"string\") {\n          classes.push(newClass);\n        }\n      }\n    }\n    \n    // make sure that wysihtml5 temp class doesn't get stripped out\n    allowedClasses[\"_wysihtml5-temp-placeholder\"] = 1;\n    \n    // add old classes last\n    oldClasses = oldNode.getAttribute(\"class\");\n    if (oldClasses) {\n      classes = classes.concat(oldClasses.split(WHITE_SPACE_REG_EXP));\n    }\n    classesLength = classes.length;\n    for (; i<classesLength; i++) {\n      currentClass = classes[i];\n      if (allowedClasses[currentClass]) {\n        newClasses.push(currentClass);\n      }\n    }\n    \n    // remove duplicate entries and preserve class specificity\n    newClassesLength = newClasses.length;\n    while (newClassesLength--) {\n      currentClass = newClasses[newClassesLength];\n      if (!wysihtml5.lang.array(newUniqueClasses).contains(currentClass)) {\n        newUniqueClasses.unshift(currentClass);\n      }\n    }\n    \n    if (newUniqueClasses.length) {\n      attributes[\"class\"] = newUniqueClasses.join(\" \");\n    }\n    \n    // set attributes on newNode\n    for (attributeName in attributes) {\n      // Setting attributes can cause a js error in IE under certain circumstances\n      // eg. on a <img> under https when it's new attribute value is non-https\n      // TODO: Investigate this further and check for smarter handling\n      try {\n        newNode.setAttribute(attributeName, attributes[attributeName]);\n      } catch(e) {}\n    }\n    \n    // IE8 sometimes loses the width/height attributes when those are set before the \"src\"\n    // so we make sure to set them again\n    if (attributes.src) {\n      if (typeof(attributes.width) !== \"undefined\") {\n        newNode.setAttribute(\"width\", attributes.width);\n      }\n      if (typeof(attributes.height) !== \"undefined\") {\n        newNode.setAttribute(\"height\", attributes.height);\n      }\n    }\n  }\n  \n  /**\n   * IE gives wrong results for hasAttribute/getAttribute, for example:\n   *    var td = document.createElement(\"td\");\n   *    td.getAttribute(\"rowspan\"); // => \"1\" in IE\n   *\n   * Therefore we have to check the element's outerHTML for the attribute\n   */\n  var HAS_GET_ATTRIBUTE_BUG = !wysihtml5.browser.supportsGetAttributeCorrectly();\n  function _getAttribute(node, attributeName) {\n    attributeName = attributeName.toLowerCase();\n    var nodeName = node.nodeName;\n    if (nodeName == \"IMG\" && attributeName == \"src\" && _isLoadedImage(node) === true) {\n      // Get 'src' attribute value via object property since this will always contain the\n      // full absolute url (http://...)\n      // this fixes a very annoying bug in firefox (ver 3.6 & 4) and IE 8 where images copied from the same host\n      // will have relative paths, which the sanitizer strips out (see attributeCheckMethods.url)\n      return node.src;\n    } else if (HAS_GET_ATTRIBUTE_BUG && \"outerHTML\" in node) {\n      // Don't trust getAttribute/hasAttribute in IE 6-8, instead check the element's outerHTML\n      var outerHTML      = node.outerHTML.toLowerCase(),\n          // TODO: This might not work for attributes without value: <input disabled>\n          hasAttribute   = outerHTML.indexOf(\" \" + attributeName +  \"=\") != -1;\n      \n      return hasAttribute ? node.getAttribute(attributeName) : null;\n    } else{\n      return node.getAttribute(attributeName);\n    }\n  }\n  \n  /**\n   * Check whether the given node is a proper loaded image\n   * FIXME: Returns undefined when unknown (Chrome, Safari)\n   */\n  function _isLoadedImage(node) {\n    try {\n      return node.complete && !node.mozMatchesSelector(\":-moz-broken\");\n    } catch(e) {\n      if (node.complete && node.readyState === \"complete\") {\n        return true;\n      }\n    }\n  }\n  \n  function _handleText(oldNode) {\n    return oldNode.ownerDocument.createTextNode(oldNode.data);\n  }\n  \n  \n  // ------------ attribute checks ------------ \\\\\n  var attributeCheckMethods = {\n    url: (function() {\n      var REG_EXP = /^https?:\\/\\//i;\n      return function(attributeValue) {\n        if (!attributeValue || !attributeValue.match(REG_EXP)) {\n          return null;\n        }\n        return attributeValue.replace(REG_EXP, function(match) {\n          return match.toLowerCase();\n        });\n      };\n    })(),\n    \n    alt: (function() {\n      var REG_EXP = /[^ a-z0-9_\\-]/gi;\n      return function(attributeValue) {\n        if (!attributeValue) {\n          return \"\";\n        }\n        return attributeValue.replace(REG_EXP, \"\");\n      };\n    })(),\n    \n    numbers: (function() {\n      var REG_EXP = /\\D/g;\n      return function(attributeValue) {\n        attributeValue = (attributeValue || \"\").replace(REG_EXP, \"\");\n        return attributeValue || null;\n      };\n    })()\n  };\n  \n  // ------------ class converter (converts an html attribute to a class name) ------------ \\\\\n  var addClassMethods = {\n    align_img: (function() {\n      var mapping = {\n        left:   \"wysiwyg-float-left\",\n        right:  \"wysiwyg-float-right\"\n      };\n      return function(attributeValue) {\n        return mapping[String(attributeValue).toLowerCase()];\n      };\n    })(),\n    \n    align_text: (function() {\n      var mapping = {\n        left:     \"wysiwyg-text-align-left\",\n        right:    \"wysiwyg-text-align-right\",\n        center:   \"wysiwyg-text-align-center\",\n        justify:  \"wysiwyg-text-align-justify\"\n      };\n      return function(attributeValue) {\n        return mapping[String(attributeValue).toLowerCase()];\n      };\n    })(),\n    \n    clear_br: (function() {\n      var mapping = {\n        left:   \"wysiwyg-clear-left\",\n        right:  \"wysiwyg-clear-right\",\n        both:   \"wysiwyg-clear-both\",\n        all:    \"wysiwyg-clear-both\"\n      };\n      return function(attributeValue) {\n        return mapping[String(attributeValue).toLowerCase()];\n      };\n    })(),\n    \n    size_font: (function() {\n      var mapping = {\n        \"1\": \"wysiwyg-font-size-xx-small\",\n        \"2\": \"wysiwyg-font-size-small\",\n        \"3\": \"wysiwyg-font-size-medium\",\n        \"4\": \"wysiwyg-font-size-large\",\n        \"5\": \"wysiwyg-font-size-x-large\",\n        \"6\": \"wysiwyg-font-size-xx-large\",\n        \"7\": \"wysiwyg-font-size-xx-large\",\n        \"-\": \"wysiwyg-font-size-smaller\",\n        \"+\": \"wysiwyg-font-size-larger\"\n      };\n      return function(attributeValue) {\n        return mapping[String(attributeValue).charAt(0)];\n      };\n    })()\n  };\n  \n  return parse;\n})();/**\n * Checks for empty text node childs and removes them\n *\n * @param {Element} node The element in which to cleanup\n * @example\n *    wysihtml5.dom.removeEmptyTextNodes(element);\n */\nwysihtml5.dom.removeEmptyTextNodes = function(node) {\n  var childNode,\n      childNodes        = wysihtml5.lang.array(node.childNodes).get(),\n      childNodesLength  = childNodes.length,\n      i                 = 0;\n  for (; i<childNodesLength; i++) {\n    childNode = childNodes[i];\n    if (childNode.nodeType === wysihtml5.TEXT_NODE && childNode.data === \"\") {\n      childNode.parentNode.removeChild(childNode);\n    }\n  }\n};\n/**\n * Renames an element (eg. a <div> to a <p>) and keeps its childs\n *\n * @param {Element} element The list element which should be renamed\n * @param {Element} newNodeName The desired tag name\n *\n * @example\n *    <!-- Assume the following dom: -->\n *    <ul id=\"list\">\n *      <li>eminem</li>\n *      <li>dr. dre</li>\n *      <li>50 Cent</li>\n *    </ul>\n *\n *    <script>\n *      wysihtml5.dom.renameElement(document.getElementById(\"list\"), \"ol\");\n *    </script>\n *\n *    <!-- Will result in: -->\n *    <ol>\n *      <li>eminem</li>\n *      <li>dr. dre</li>\n *      <li>50 Cent</li>\n *    </ol>\n */\nwysihtml5.dom.renameElement = function(element, newNodeName) {\n  var newElement = element.ownerDocument.createElement(newNodeName),\n      firstChild;\n  while (firstChild = element.firstChild) {\n    newElement.appendChild(firstChild);\n  }\n  wysihtml5.dom.copyAttributes([\"align\", \"className\"]).from(element).to(newElement);\n  element.parentNode.replaceChild(newElement, element);\n  return newElement;\n};/**\n * Takes an element, removes it and replaces it with it's childs\n * \n * @param {Object} node The node which to replace with it's child nodes\n * @example\n *    <div id=\"foo\">\n *      <span>hello</span>\n *    </div>\n *    <script>\n *      // Remove #foo and replace with it's children\n *      wysihtml5.dom.replaceWithChildNodes(document.getElementById(\"foo\"));\n *    </script>\n */\nwysihtml5.dom.replaceWithChildNodes = function(node) {\n  if (!node.parentNode) {\n    return;\n  }\n  \n  if (!node.firstChild) {\n    node.parentNode.removeChild(node);\n    return;\n  }\n  \n  var fragment = node.ownerDocument.createDocumentFragment();\n  while (node.firstChild) {\n    fragment.appendChild(node.firstChild);\n  }\n  node.parentNode.replaceChild(fragment, node);\n  node = fragment = null;\n};\n/**\n * Unwraps an unordered/ordered list\n *\n * @param {Element} element The list element which should be unwrapped\n *\n * @example\n *    <!-- Assume the following dom: -->\n *    <ul id=\"list\">\n *      <li>eminem</li>\n *      <li>dr. dre</li>\n *      <li>50 Cent</li>\n *    </ul>\n *\n *    <script>\n *      wysihtml5.dom.resolveList(document.getElementById(\"list\"));\n *    </script>\n *\n *    <!-- Will result in: -->\n *    eminem<br>\n *    dr. dre<br>\n *    50 Cent<br>\n */\n(function(dom) {\n  function _isBlockElement(node) {\n    return dom.getStyle(\"display\").from(node) === \"block\";\n  }\n  \n  function _isLineBreak(node) {\n    return node.nodeName === \"BR\";\n  }\n  \n  function _appendLineBreak(element) {\n    var lineBreak = element.ownerDocument.createElement(\"br\");\n    element.appendChild(lineBreak);\n  }\n  \n  function resolveList(list) {\n    if (list.nodeName !== \"MENU\" && list.nodeName !== \"UL\" && list.nodeName !== \"OL\") {\n      return;\n    }\n    \n    var doc             = list.ownerDocument,\n        fragment        = doc.createDocumentFragment(),\n        previousSibling = list.previousElementSibling || list.previousSibling,\n        firstChild,\n        lastChild,\n        isLastChild,\n        shouldAppendLineBreak,\n        listItem;\n    \n    if (previousSibling && !_isBlockElement(previousSibling)) {\n      _appendLineBreak(fragment);\n    }\n    \n    while (listItem = list.firstChild) {\n      lastChild = listItem.lastChild;\n      while (firstChild = listItem.firstChild) {\n        isLastChild           = firstChild === lastChild;\n        // This needs to be done before appending it to the fragment, as it otherwise will loose style information\n        shouldAppendLineBreak = isLastChild && !_isBlockElement(firstChild) && !_isLineBreak(firstChild);\n        fragment.appendChild(firstChild);\n        if (shouldAppendLineBreak) {\n          _appendLineBreak(fragment);\n        }\n      }\n      \n      listItem.parentNode.removeChild(listItem);\n    }\n    list.parentNode.replaceChild(fragment, list);\n  }\n  \n  dom.resolveList = resolveList;\n})(wysihtml5.dom);/**\n * Sandbox for executing javascript, parsing css styles and doing dom operations in a secure way\n *\n * Browser Compatibility:\n *  - Secure in MSIE 6+, but only when the user hasn't made changes to his security level \"restricted\"\n *  - Partially secure in other browsers (Firefox, Opera, Safari, Chrome, ...)\n *\n * Please note that this class can't benefit from the HTML5 sandbox attribute for the following reasons:\n *    - sandboxing doesn't work correctly with inlined content (src=\"javascript:'<html>...</html>'\")\n *    - sandboxing of physical documents causes that the dom isn't accessible anymore from the outside (iframe.contentWindow, ...)\n *    - setting the \"allow-same-origin\" flag would fix that, but then still javascript and dom events refuse to fire\n *    - therefore the \"allow-scripts\" flag is needed, which then would deactivate any security, as the js executed inside the iframe\n *      can do anything as if the sandbox attribute wasn't set\n *\n * @param {Function} [readyCallback] Method that gets invoked when the sandbox is ready\n * @param {Object} [config] Optional parameters\n *\n * @example\n *    new wysihtml5.dom.Sandbox(function(sandbox) {\n *      sandbox.getWindow().document.body.innerHTML = '<img src=foo.gif onerror=\"alert(document.cookie)\">';\n *    });\n */\n(function(wysihtml5) {\n  var /**\n       * Default configuration\n       */\n      doc                 = document,\n      /**\n       * Properties to unset/protect on the window object\n       */\n      windowProperties    = [\n        \"parent\", \"top\", \"opener\", \"frameElement\", \"frames\",\n        \"localStorage\", \"globalStorage\", \"sessionStorage\", \"indexedDB\"\n      ],\n      /**\n       * Properties on the window object which are set to an empty function\n       */\n      windowProperties2   = [\n        \"open\", \"close\", \"openDialog\", \"showModalDialog\",\n        \"alert\", \"confirm\", \"prompt\",\n        \"openDatabase\", \"postMessage\",\n        \"XMLHttpRequest\", \"XDomainRequest\"\n      ],\n      /**\n       * Properties to unset/protect on the document object\n       */\n      documentProperties  = [\n        \"referrer\",\n        \"write\", \"open\", \"close\"\n      ];\n  \n  wysihtml5.dom.Sandbox = Base.extend(\n    /** @scope wysihtml5.dom.Sandbox.prototype */ {\n\n    constructor: function(readyCallback, config) {\n      this.callback = readyCallback || wysihtml5.EMPTY_FUNCTION;\n      this.config   = wysihtml5.lang.object({}).merge(config).get();\n      this.iframe   = this._createIframe();\n    },\n    \n    insertInto: function(element) {\n      if (typeof(element) === \"string\") {\n        element = doc.getElementById(element);\n      }\n      \n      element.appendChild(this.iframe);\n    },\n\n    getIframe: function() {\n      return this.iframe;\n    },\n\n    getWindow: function() {\n      this._readyError();\n    },\n\n    getDocument: function() {\n      this._readyError();\n    },\n\n    destroy: function() {\n      var iframe = this.getIframe();\n      iframe.parentNode.removeChild(iframe);\n    },\n\n    _readyError: function() {\n      throw new Error(\"wysihtml5.Sandbox: Sandbox iframe isn't loaded yet\");\n    },\n\n    /**\n     * Creates the sandbox iframe\n     *\n     * Some important notes:\n     *  - We can't use HTML5 sandbox for now:\n     *    setting it causes that the iframe's dom can't be accessed from the outside\n     *    Therefore we need to set the \"allow-same-origin\" flag which enables accessing the iframe's dom\n     *    But then there's another problem, DOM events (focus, blur, change, keypress, ...) aren't fired.\n     *    In order to make this happen we need to set the \"allow-scripts\" flag.\n     *    A combination of allow-scripts and allow-same-origin is almost the same as setting no sandbox attribute at all.\n     *  - Chrome & Safari, doesn't seem to support sandboxing correctly when the iframe's html is inlined (no physical document)\n     *  - IE needs to have the security=\"restricted\" attribute set before the iframe is \n     *    inserted into the dom tree\n     *  - Believe it or not but in IE \"security\" in document.createElement(\"iframe\") is false, even\n     *    though it supports it\n     *  - When an iframe has security=\"restricted\", in IE eval() & execScript() don't work anymore\n     *  - IE doesn't fire the onload event when the content is inlined in the src attribute, therefore we rely\n     *    on the onreadystatechange event\n     */\n    _createIframe: function() {\n      var that   = this,\n          iframe = doc.createElement(\"iframe\");\n      iframe.className = \"wysihtml5-sandbox\";\n      wysihtml5.dom.setAttributes({\n        \"security\":           \"restricted\",\n        \"allowtransparency\":  \"true\",\n        \"frameborder\":        0,\n        \"width\":              0,\n        \"height\":             0,\n        \"marginwidth\":        0,\n        \"marginheight\":       0\n      }).on(iframe);\n\n      // Setting the src like this prevents ssl warnings in IE6\n      if (wysihtml5.browser.throwsMixedContentWarningWhenIframeSrcIsEmpty()) {\n        iframe.src = \"javascript:'<html></html>'\";\n      }\n\n      iframe.onload = function() {\n        iframe.onreadystatechange = iframe.onload = null;\n        that._onLoadIframe(iframe);\n      };\n\n      iframe.onreadystatechange = function() {\n        if (/loaded|complete/.test(iframe.readyState)) {\n          iframe.onreadystatechange = iframe.onload = null;\n          that._onLoadIframe(iframe);\n        }\n      };\n\n      return iframe;\n    },\n\n    /**\n     * Callback for when the iframe has finished loading\n     */\n    _onLoadIframe: function(iframe) {\n      // don't resume when the iframe got unloaded (eg. by removing it from the dom)\n      if (!wysihtml5.dom.contains(doc.documentElement, iframe)) {\n        return;\n      }\n\n      var that           = this,\n          iframeWindow   = iframe.contentWindow,\n          iframeDocument = iframe.contentWindow.document,\n          charset        = doc.characterSet || doc.charset || \"utf-8\",\n          sandboxHtml    = this._getHtml({\n            charset:      charset,\n            stylesheets:  this.config.stylesheets\n          });\n\n      // Create the basic dom tree including proper DOCTYPE and charset\n      iframeDocument.open(\"text/html\", \"replace\");\n      iframeDocument.write(sandboxHtml);\n      iframeDocument.close();\n\n      this.getWindow = function() { return iframe.contentWindow; };\n      this.getDocument = function() { return iframe.contentWindow.document; };\n\n      // Catch js errors and pass them to the parent's onerror event\n      // addEventListener(\"error\") doesn't work properly in some browsers\n      // TODO: apparently this doesn't work in IE9!\n      iframeWindow.onerror = function(errorMessage, fileName, lineNumber) {\n        throw new Error(\"wysihtml5.Sandbox: \" + errorMessage, fileName, lineNumber);\n      };\n\n      if (!wysihtml5.browser.supportsSandboxedIframes()) {\n        // Unset a bunch of sensitive variables\n        // Please note: This isn't hack safe!  \n        // It more or less just takes care of basic attacks and prevents accidental theft of sensitive information\n        // IE is secure though, which is the most important thing, since IE is the only browser, who\n        // takes over scripts & styles into contentEditable elements when copied from external websites\n        // or applications (Microsoft Word, ...)\n        var i, length;\n        for (i=0, length=windowProperties.length; i<length; i++) {\n          this._unset(iframeWindow, windowProperties[i]);\n        }\n        for (i=0, length=windowProperties2.length; i<length; i++) {\n          this._unset(iframeWindow, windowProperties2[i], wysihtml5.EMPTY_FUNCTION);\n        }\n        for (i=0, length=documentProperties.length; i<length; i++) {\n          this._unset(iframeDocument, documentProperties[i]);\n        }\n        // This doesn't work in Safari 5 \n        // See http://stackoverflow.com/questions/992461/is-it-possible-to-override-document-cookie-in-webkit\n        this._unset(iframeDocument, \"cookie\", \"\", true);\n      }\n\n      this.loaded = true;\n\n      // Trigger the callback\n      setTimeout(function() { that.callback(that); }, 0);\n    },\n\n    _getHtml: function(templateVars) {\n      var stylesheets = templateVars.stylesheets,\n          html        = \"\",\n          i           = 0,\n          length;\n      stylesheets = typeof(stylesheets) === \"string\" ? [stylesheets] : stylesheets;\n      if (stylesheets) {\n        length = stylesheets.length;\n        for (; i<length; i++) {\n          html += '<link rel=\"stylesheet\" href=\"' + stylesheets[i] + '\">';\n        }\n      }\n      templateVars.stylesheets = html;\n\n      return wysihtml5.lang.string(\n        '<!DOCTYPE html><html><head>'\n        + '<meta charset=\"#{charset}\">#{stylesheets}</head>'\n        + '<body></body></html>'\n      ).interpolate(templateVars);\n    },\n\n    /**\n     * Method to unset/override existing variables\n     * @example\n     *    // Make cookie unreadable and unwritable\n     *    this._unset(document, \"cookie\", \"\", true);\n     */\n    _unset: function(object, property, value, setter) {\n      try { object[property] = value; } catch(e) {}\n\n      try { object.__defineGetter__(property, function() { return value; }); } catch(e) {}\n      if (setter) {\n        try { object.__defineSetter__(property, function() {}); } catch(e) {}\n      }\n\n      if (!wysihtml5.browser.crashesWhenDefineProperty(property)) {\n        try {\n          var config = {\n            get: function() { return value; }\n          };\n          if (setter) {\n            config.set = function() {};\n          }\n          Object.defineProperty(object, property, config);\n        } catch(e) {}\n      }\n    }\n  });\n})(wysihtml5);\n(function() {\n  var mapping = {\n    \"className\": \"class\"\n  };\n  wysihtml5.dom.setAttributes = function(attributes) {\n    return {\n      on: function(element) {\n        for (var i in attributes) {\n          element.setAttribute(mapping[i] || i, attributes[i]);\n        }\n      }\n    }\n  };\n})();wysihtml5.dom.setStyles = function(styles) {\n  return {\n    on: function(element) {\n      var style = element.style;\n      if (typeof(styles) === \"string\") {\n        style.cssText += \";\" + styles;\n        return;\n      }\n      for (var i in styles) {\n        if (i === \"float\") {\n          style.cssFloat = styles[i];\n          style.styleFloat = styles[i];\n        } else {\n          style[i] = styles[i];\n        }\n      }\n    }\n  };\n};/**\n * Simulate HTML5 placeholder attribute\n *\n * Needed since\n *    - div[contentEditable] elements don't support it\n *    - older browsers (such as IE8 and Firefox 3.6) don't support it at all\n *\n * @param {Object} parent Instance of main wysihtml5.Editor class\n * @param {Element} view Instance of wysihtml5.views.* class\n * @param {String} placeholderText\n *\n * @example\n *    wysihtml.dom.simulatePlaceholder(this, composer, \"Foobar\");\n */\n(function(dom) {\n  dom.simulatePlaceholder = function(editor, view, placeholderText) {\n    var CLASS_NAME = \"placeholder\",\n        unset = function() {\n          if (view.hasPlaceholderSet()) {\n            view.clear();\n          }\n          dom.removeClass(view.element, CLASS_NAME);\n        },\n        set = function() {\n          if (view.isEmpty()) {\n            view.setValue(placeholderText);\n            dom.addClass(view.element, CLASS_NAME);\n          }\n        };\n\n    editor\n      .observe(\"set_placeholder\", set)\n      .observe(\"unset_placeholder\", unset)\n      .observe(\"focus:composer\", unset)\n      .observe(\"paste:composer\", unset)\n      .observe(\"blur:composer\", set);\n\n    set();\n  };\n})(wysihtml5.dom);\n(function(dom) {\n  var documentElement = document.documentElement;\n  if (\"textContent\" in documentElement) {\n    dom.setTextContent = function(element, text) {\n      element.textContent = text;\n    };\n\n    dom.getTextContent = function(element) {\n      return element.textContent;\n    };\n  } else if (\"innerText\" in documentElement) {\n    dom.setTextContent = function(element, text) {\n      element.innerText = text;\n    };\n\n    dom.getTextContent = function(element) {\n      return element.innerText;\n    };\n  } else {\n    dom.setTextContent = function(element, text) {\n      element.nodeValue = text;\n    };\n\n    dom.getTextContent = function(element) {\n      return element.nodeValue;\n    };\n  }\n})(wysihtml5.dom);\n\n/**\n * Fix most common html formatting misbehaviors of browsers implementation when inserting\n * content via copy & paste contentEditable\n *\n * @author Christopher Blum\n */\nwysihtml5.quirks.cleanPastedHTML = (function() {\n  // TODO: We probably need more rules here\n  var defaultRules = {\n    // When pasting underlined links <a> into a contentEditable, IE thinks, it has to insert <u> to keep the styling\n    \"a u\": wysihtml5.dom.replaceWithChildNodes\n  };\n  \n  function cleanPastedHTML(elementOrHtml, rules, context) {\n    rules   = rules || defaultRules;\n    context = context || elementOrHtml.ownerDocument || document;\n    \n    var element,\n        isString = typeof(elementOrHtml) === \"string\",\n        method,\n        matches,\n        matchesLength,\n        i,\n        j = 0;\n    if (isString) {\n      element = wysihtml5.dom.getAsDom(elementOrHtml, context);\n    } else {\n      element = elementOrHtml;\n    }\n    \n    for (i in rules) {\n      matches       = element.querySelectorAll(i);\n      method        = rules[i];\n      matchesLength = matches.length;\n      for (; j<matchesLength; j++) {\n        method(matches[j]);\n      }\n    }\n    \n    matches = elementOrHtml = rules = null;\n    \n    return isString ? element.innerHTML : element;\n  }\n  \n  return cleanPastedHTML;\n})();/**\n * IE and Opera leave an empty paragraph in the contentEditable element after clearing it\n *\n * @param {Object} contentEditableElement The contentEditable element to observe for clearing events\n * @exaple\n *    wysihtml5.quirks.ensureProperClearing(myContentEditableElement);\n */\n(function(wysihtml5) {\n  var dom = wysihtml5.dom;\n  \n  wysihtml5.quirks.ensureProperClearing = (function() {\n    var clearIfNecessary = function(event) {\n      var element = this;\n      setTimeout(function() {\n        var innerHTML = element.innerHTML.toLowerCase();\n        if (innerHTML == \"<p>&nbsp;</p>\" ||\n            innerHTML == \"<p>&nbsp;</p><p>&nbsp;</p>\") {\n          element.innerHTML = \"\";\n        }\n      }, 0);\n    };\n\n    return function(composer) {\n      dom.observe(composer.element, [\"cut\", \"keydown\"], clearIfNecessary);\n    };\n  })();\n\n\n\n  /**\n   * In Opera when the caret is in the first and only item of a list (<ul><li>|</li></ul>) and the list is the first child of the contentEditable element, it's impossible to delete the list by hitting backspace\n   *\n   * @param {Object} contentEditableElement The contentEditable element to observe for clearing events\n has no method 'replace'
W20150401-15:27:19.902(3)? (STDERR)     at ServerResponse.http.OutgoingMessage.write (packages/meteorhacks:inject-data/lib/server.js:48:1)
W20150401-15:27:19.903(3)? (STDERR)     at ServerResponse.res.write (/Users/mike/.meteor/packages/webapp/.1.2.0.awacga++os+web.browser+web.cordova/npm/node_modules/connect/lib/middleware/compress.js:110:17)
W20150401-15:27:19.903(3)? (STDERR)     at write (_stream_readable.js:602:24)
W20150401-15:27:19.903(3)? (STDERR)     at flow (_stream_readable.js:611:7)
W20150401-15:27:19.903(3)? (STDERR)     at ReadStream.pipeOnReadable (_stream_readable.js:643:5)
W20150401-15:27:19.903(3)? (STDERR)     at ReadStream.emit (events.js:92:17)
W20150401-15:27:19.903(3)? (STDERR)     at emitReadable_ (_stream_readable.js:427:10)
W20150401-15:27:19.903(3)? (STDERR)     at emitReadable (_stream_readable.js:423:5)
W20150401-15:27:19.903(3)? (STDERR)     at readableAddChunk (_stream_readable.js:166:9)
W20150401-15:27:19.903(3)? (STDERR)     at ReadStream.Readable.push (_stream_readable.js:128:10)

[email protected] + doesn't work any more

Example from docs :

Picker.route("/", function(params, req, res, next) {
  var ejsonData = {aa: 10};
  InjectData.pushData(res, "some-key", ejsonData);
  // make sure to move the routing forward.
  next();
});

AND

InjectData.getData("some-key", function(data) {
  console.log(data);
});

doesn't work.
On client data is undefined and should be {{aa:10}}

What is more important, this bug breaks FastRender !

inject-data breaks practicalmeteor:mocha (and others)

I've spent quite a bit of time off and on trying to get a test suite to work on our app, and after trying several different test runners and a lot of debugging, what I've discovered is that the reason I haven't been able to get it to work is that inject-data is basically breaking anything that uses mocha.

Here is what's happening in my case:

Mocha includes jade, which includes this code in lib/filters.js:

  coffeescript: function(str){
    str = str.replace(/\\n/g, '\n');
    var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
    return '<script type="text/javascript">\\n' + js + '</script>';
  }

Jade also includes, in lib/doctypes.js:

module.exports = { 
    '5': '<!DOCTYPE html>'
  , 'default': '<!DOCTYPE html>'

So, when meteor concatenates these files together and serves them, because inject-data is using /<!DOCTYPE html>/.test(chunk) to determine when it needs to inject, it's attempting to inject into the practicalmeteor_mocha-core.js file.

This actually wouldn't be a huge problem in this particular case, because I don't think there is anything in practicalmeteor:mocha that would actually use that coffeescript filter, so it would just be some unused code bloat. However, since inject-data is loading lib/inject.html as an asset, the template being used for injection ends with a newline, and it's actually the newline that is causing all my failures..

  coffeescript: function(str){
    str = str.replace(/\\n/g, '\n');
    var js = require('coffee-script').compile(str).replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
    return '<script type="text/inject-data">%7B%22fast-render-data%22%3A%7B%22collectionData%22%3A%7B%7D%2C%22subscriptions%22%3A%7B%7D%2C%22loginToken%22%3A%...%22%7D%7D</script>
<script type="text/javascript">\\n' + js + '</script>';
  } 

And in the end, my entire test suite is failing because single-quoted javascript strings can't contain literal new-lines.

Now that I've tracked this down in my own code, I see that several other people have already tried to fix it -- any of the four currently open PR's (#2 #5 #6 #7) would have avoided this problem for me, either by not injecting into that file, or by not including the newline from the template.

This package is randomly causing my servers to crash and restart

This is the error I see in my logs:

uncaughtException: Cannot read property 'access-control-allow-origin' of null 
    { date: 'Sun Nov 06 2016 05:06:24 GMT+0000 (UTC)', 
      process:  
       { pid: 139, 
         uid: 0, 
         gid: 0, 
         cwd: '/bundle/bundle/programs/server', 
         execPath: '/opt/nodejs/bin/node', 
         version: 'v4.4.7', 
         argv:  
          [ '/opt/nodejs/bin/node', 
            '/bundle/bundle/main.js', 
            'program.json' ], 
         memoryUsage: { rss: 383619072, heapTotal: 330623328, heapUsed: 299786400 } }, 
      os:  
       { loadavg: [ 0.2236328125, 0.0810546875, 0.11279296875 ], 
         uptime: 2327807 }, 
      trace:  
       [ { column: 22, 
           file: 'packages/meteorhacks_inject-data.js', 
           function: 'ServerResponse.res.write', 
           line: 108, 
           method: 'res.write', 
           native: false }, 
         { column: 10, 
           file: '_http_outgoing.js', 
           function: 'ServerResponse.OutgoingMessage.end', 
           line: 588, 
           method: 'OutgoingMessage.end', 
           native: false }, 
         { column: 20, 
           file: '/bundle/bundle/programs/server/npm/node_modules/prerender-node/index.js', 
           function: null, 
           line: 29, 
           method: null, 
           native: false }, 
         { column: 5, 
           file: '/bundle/bundle/programs/server/npm/node_modules/prerender-node/index.js', 
           function: '', 
           line: 210, 
           method: null, 
           native: false }, 
         { column: 20, 
           file: 'events.js', 
           function: 'emitNone', 
           line: 72, 
           method: null, 
           native: false }, 
         { column: 7, 
           file: 'events.js', 
           function: 'Gunzip.emit', 
           line: 166, 
           method: 'emit', 
           native: false }, 
         { column: 12, 
           file: '_stream_readable.js', 
           function: 'endReadableNT', 
           line: 921, 
           method: null, 
           native: false }, 
         { column: 9, 
           file: 'node.js', 
           function: 'nextTickCallbackWith2Args', 
           line: 442, 
           method: null, 
           native: false }, 
         { column: 17, 
           file: 'node.js', 
           function: 'process._tickCallback', 
           line: 356, 
           method: '_tickCallback', 
           native: false } ], 
      stack:  
       [ 'TypeError: Cannot read property \'access-control-allow-origin\' of null', 
         '    at ServerResponse.res.write (packages/meteorhacks_inject-data.js:108:22)', 
         '    at ServerResponse.OutgoingMessage.end (_http_outgoing.js:588:10)', 
         '    at /bundle/bundle/programs/server/npm/node_modules/prerender-node/index.js:29:20', 
         '    at Gunzip.<anonymous> (/bundle/bundle/programs/server/npm/node_modules/prerender-node/index.js:210:5)', 
         '    at emitNone (events.js:72:20)', 
         '    at Gunzip.emit (events.js:166:7)', 
         '    at endReadableNT (_stream_readable.js:921:12)', 
         '    at nextTickCallbackWith2Args (node.js:442:9)', 
         '    at process._tickCallback (node.js:356:17)' ] } 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.