[
  {
    "path": ".gitignore",
    "content": ".DS_Store\ndist/"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2009-2011 Amy Hoy & Thomas Fuchs\nhttp://mir.aculo.us/dom-monster\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.rdoc",
    "content": "== DOM Monster: a cross-platform, cross-browser bookmarklet that will analyze the DOM & other features of the page you're on, and give you its bill of health\n\nJust install the DOM Monster bookmarklet, and use it on any web page or app.\nIf there are problems, DOM Monster will point them out and even make suggestions on how to fix 'em.\n\nLearn more about the DOM Monster at\nhttp://mir.aculo.us/dom-monster\n\n= License\n\nDOM Monster is is licensed under the terms of the MIT License, see the included MIT-LICENSE file.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'rake'\n\nDOMMONSTER_VERSION  = \"1.3.0\"\n\nDOMMONSTER_ROOT     = File.expand_path(File.dirname(__FILE__))\nDOMMONSTER_SRC_DIR  = File.join(DOMMONSTER_ROOT, 'src')\nDOMMONSTER_DIST_DIR = File.join(DOMMONSTER_ROOT, 'dist')\n\nDOMMONSTER_FILES    = [\n  File.join(DOMMONSTER_SRC_DIR,'dommonster.js'),\n]\n\ntask :default => [:clean, :concat, :dist]\n\ndesc \"Clean the distribution directory.\"\ntask :clean do\n  rm_rf DOMMONSTER_DIST_DIR\n  mkdir DOMMONSTER_DIST_DIR\nend\n\ndef normalize_whitespace(filename)\n  contents = File.readlines(filename)\n  contents.each { |line| line.sub!(/\\s+$/, \"\") }\n  File.open(filename, \"w\") do |file|\n    file.write contents.join(\"\\n\").sub(/(\\n+)?\\Z/m, \"\\n\")\n  end\nend\n\ndesc \"Strip trailing whitespace and ensure each file ends with a newline\"\ntask :whitespace do\n  Dir[\"*\", \"src/**/*\", \"test/**/*\", \"examples/**/*\"].each do |filename|\n    normalize_whitespace(filename) if File.file?(filename)\n  end\nend\n\ndesc \"Concatenate DOM Monster files to build a distributable dommonster.js file\"\ntask :concat => :whitespace do\n  File.open(File.join(DOMMONSTER_DIST_DIR,'dommonster.js'),\"w\") do |f|\n    f.puts DOMMONSTER_FILES.map{ |s| IO.read(s) }\n  end\nend\n\ndef uglifyjs(src, target)\n  begin\n    require 'uglifier'\n  rescue LoadError => e\n    if verbose\n      puts \"\\nYou'll need the 'uglifier' gem for minification. Just run:\\n\\n\"\n      puts \"  $ gem install uglifier\"\n      puts \"\\nand you should be all set.\\n\\n\"\n      exit\n    end\n    return false\n  end\n  puts \"Minifying #{src} with UglifyJS...\"\n  File.open(target, \"w\"){|f| f.puts Uglifier.new.compile(File.read(src))}\nend\n\ndef process_minified(src, target)\n  cp target, File.join(DOMMONSTER_DIST_DIR,'temp.js')\n  msize = File.size(File.join(DOMMONSTER_DIST_DIR,'temp.js'))\n  `gzip -9 #{File.join(DOMMONSTER_DIST_DIR,'temp.js')}`\n\n  osize = File.size(src)\n  dsize = File.size(File.join(DOMMONSTER_DIST_DIR,'temp.js.gz'))\n  rm_rf File.join(DOMMONSTER_DIST_DIR,'temp.js.gz')\n\n  puts \"Original version: %.3fk\" % (osize/1024.0)\n  puts \"Minified: %.3fk\" % (msize/1024.0)\n  puts \"Minified and gzipped: %.3fk, compression factor %.3f\" % [dsize/1024.0, osize/dsize.to_f]\nend\n\ndesc \"Generates a minified version for distribution.\"\ntask :dist do\n  src, target = File.join(DOMMONSTER_DIST_DIR,'dommonster.js'), File.join(DOMMONSTER_DIST_DIR,'dommonster.min.js')\n  uglifyjs src, target\n  process_minified src, target\nend\n"
  },
  {
    "path": "bookmarklet-local.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>DOM Monster Bookmarklet</title>\n  <meta http-equiv=\"imagetoolbar\" content=\"no\" />\n  <meta charset=\"utf-8\" />\n  <style media=\"screen\">\n    body {\n      font:14px/18px 'Lucida Grande', Verdana, sans-serif;\n      margin: 40px 0 25px 50px;\n    }\n    a {\n      color: #5d2a10;\n    }\n    h2 {\n      margin:0;\n      width:618px;\n      height:174px;\n      text-indent:-9999em;\n      background:url(assets/dom-monster.png);\n    }\n    p {\n      width:510px;\n      font-size:14px;\n      line-height:20px;\n      margin-left: 175px;\n      color:#444;\n    }\n    p.important {\n      font-size:17px;\n      line-height:21px;\n      color: #5d2a10;\n    }\n    a.bookmarklet {\n      background: #5d2a10;\n      color: #fff;\n      padding: 5px;\n      -webkit-border-radius: 5px;\n      -moz-border-radius: 5px;\n      border-radius: 5px;\n    }\n    p.copyright {\n      margin-top: 30px;\n      font-size: 11px;\n      line-height: 14px;\n      color: #888;\n    }\n    em {\n      font-style: normal;\n      font-weight: bold;\n    }\n    p.learn-more {\n      border: 3px solid #f0f0f0;\n      padding: 20px;\n      width: 470px;\n      -webkit-border-radius: 20px;\n      -moz-border-radius: 20px;\n      border-radius: 20px;\n      text-align: center;\n    }\n  </style>\n</head>\n<body>\n  <h2>Meet the DOM Monster!</h2>\n  <p style=\"margin-top:-40px\">\n    <b>DOM Monster</b> is our answer to JavaScript performance tools that just don’t give you the full picture.\n  </p>\n  <p>\n    <b>DOM Monster</b> is a cross-platform, cross-browser bookmarklet that will analyze the DOM &amp; other features of the page you’re on, and give you its bill of health.\n  </p>\n  <p>\n    If there are problems, <b>DOM Monster</b> will point them out — and even make suggestions on how to fix ’em.\n  </p>\n\n  <p class=\"important\">\n    Drag the\n    <script>\n      var url = location.href.split(/\\/bookmarklet-local.html/)[0] + \"/src/dommonster.js?\";\n      document.write('<a class=\"bookmarklet\" href=\"javascript:(function(){var%20script=document.createElement(\\'script\\');script.src=\\''+url+'\\'+Math.floor((+new Date));document.body.appendChild(script);})()\">Local DOM Monster!</a>');\n    </script>\n    to your bookmarks bar! This version of the DOM Monster is loaded from your local development folder.\n  </p>\n\n  <p>\n    DOM Monster! is the brainchild of <a href=\"http://unicornfree.com/\">Amy Hoy</a> and\n    was programmed by <a href=\"http://mir.aculo.us/\">Thomas Fuchs</a>.\n  </p>\n\n  <p>\n    <em>We'll keep the DOM Monster! up to date</em> and feed it with the latest in page analyzing features for you! <em>You don't have to update or reinstall</em> the bookmarklet to always get the latest and greatest version! Be sure to drag the DOM Monster! to your bookmarks bar in <em>all browsers</em> you want to test in!\n  </p>\n\n  <p>\n    <em>The DOM Monster is open source.</em> Contribute your own tips and tests. Visit the <a href=\"http://github.com/madrobby/dom-monster\">DOM Monster repository on GitHub</a>.\n  </p>\n\n  <p class=\"learn-more\">\n    <a href=\"http://javascriptrocks.com/performance/\"><img src=\"assets/jsrocks.png\" alt=\"JavaScript Rocks!\"></a>\n    Learn more about the DOM Monster! and how to deal with DOM and JavaScript performance issues!\n    Grab a copy of our<br> <a href=\"http://javascriptrocks.com/performance/\">JavaScript Rocks! performance ebook</a>.\n  </p>\n\n  <p class=\"copyright starts-section\">\n    Copyright © 2009-2011 <a href=\"http://unicornfree.com/\">Amy Hoy</a> and <a href=\"http://mir.aculo.us/\">Thomas Fuchs</a>\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "bookmarklet.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <title>DOM Monster Bookmarklet</title>\n  <meta http-equiv=\"imagetoolbar\" content=\"no\" />\n  <meta charset=\"utf-8\" />\n  <style media=\"screen\">\n    body {\n      font:14px/18px 'Lucida Grande', Verdana, sans-serif;\n      margin: 40px 0 25px 50px;\n    }\n    a {\n      color: #5d2a10;\n    }\n    h2 {\n      margin:0;\n      width:618px;\n      height:174px;\n      text-indent:-9999em;\n      background:url(assets/dom-monster.png);\n    }\n    p {\n      width:510px;\n      font-size:14px;\n      line-height:20px;\n      margin-left: 175px;\n      color:#444;\n    }\n    p.important {\n      font-size:17px;\n      line-height:21px;\n      color: #5d2a10;\n    }\n    a.bookmarklet {\n      background: #5d2a10;\n      color: #fff;\n      padding: 5px;\n      -webkit-border-radius: 5px;\n      -moz-border-radius: 5px;\n      border-radius: 5px;\n    }\n    p.copyright {\n      margin-top: 30px;\n      font-size: 11px;\n      line-height: 14px;\n      color: #888;\n    }\n    em {\n      font-style: normal;\n      font-weight: bold;\n    }\n    p.learn-more {\n      border: 3px solid #f0f0f0;\n      padding: 20px;\n      width: 470px;\n      -webkit-border-radius: 20px;\n      -moz-border-radius: 20px;\n      border-radius: 20px;\n      text-align: center;\n    }\n  </style>\n</head>\n<body>\n  <h2>Meet the DOM Monster!</h2>\n  <p style=\"margin-top:-40px\">\n    <b>DOM Monster</b> is our answer to JavaScript performance tools that just don’t give you the full picture.\n  </p>\n  <p>\n    <b>DOM Monster</b> is a cross-platform, cross-browser bookmarklet that will analyze the DOM &amp; other features of the page you’re on, and give you its bill of health.\n  </p>\n  <p>\n    If there are problems, <b>DOM Monster</b> will point them out — and even make suggestions on how to fix ’em.\n  </p>\n\n  <p class=\"important\">\n    Drag the <a class=\"bookmarklet\" href=\"javascript:(function(){var%20script=document.createElement('script');script.src='//mir.aculo.us/dom-monster/dommonster.js?'+Math.floor((+new Date)/(864e5));document.body.appendChild(script);})()\">DOM Monster!</a> to your bookmarks bar!\n  </p>\n\n  <p>\n    DOM Monster! is the brainchild of <a href=\"http://unicornfree.com/\">Amy Hoy</a> and\n    was programmed by <a href=\"http://mir.aculo.us/\">Thomas Fuchs</a>.\n  </p>\n\n  <p>\n    <em>We'll keep the DOM Monster! up to date</em> and feed it with the latest in page analyzing features for you! <em>You don't have to update or reinstall</em> the bookmarklet to always get the latest and greatest version! Be sure to drag the DOM Monster! to your bookmarks bar in <em>all browsers</em> you want to test in!\n  </p>\n\n  <p>\n    <em>The DOM Monster is open source.</em> Contribute your own tips and tests. Visit the <a href=\"http://github.com/madrobby/dom-monster\">DOM Monster repository on GitHub</a>.\n  </p>\n\n  <p class=\"learn-more\">\n    <a href=\"http://javascriptrocks.com/performance/\"><img src=\"assets/jsrocks.png\" alt=\"JavaScript Rocks!\"></a>\n    Learn more about the DOM Monster! and how to deal with DOM and JavaScript performance issues!\n    Grab a copy of our<br> <a href=\"http://javascriptrocks.com/performance/\">JavaScript Rocks! performance ebook</a>.\n  </p>\n\n  <p class=\"copyright starts-section\">\n    Copyright © 2009-2011 <a href=\"http://unicornfree.com/\">Amy Hoy</a> and <a href=\"http://mir.aculo.us/\">Thomas Fuchs</a>\n  </p>\n</body>\n</html>\n"
  },
  {
    "path": "src/dommonster.js",
    "content": "/*\n * DOM MONSTER\n * Copyright (c) 2009-2013 Amy Hoy & Thomas Fuchs\n * This code is licensed under the terms of the MIT LICENSE\n * http://mir.aculo.us/dom-monster\n *\n * includes JAVASCRIPT STACKTRACE\n * see https://github.com/emwendelin/javascript-stacktrace\n * for license information\n */\n\n(function(){\n  var JR = { Version: '1.3.2' };\n\n  // IE does not seem to properly define the indexOf for arrays.\n  if (\"undefined\" === typeof(Array.prototype.indexOf)) {\n    Array.prototype.indexOf = function (object, index) {\n      var length = this.length;\n\n\t  index = index || 0;\n      if (index < 0) {\n        index += length;\n      }\n\n\t  for (; index < length; ++index) {\n        if (this[index] === object) {\n          return index;\n        }\n      }\n\n      return -1;\n    };\n  }\n\n  function $(id){ return document.getElementById(id); }\n\n  function $tagname(tagname) {\n    var nodes = document.getElementsByTagName(tagname),\n      retValue = [];\n\n\tfor (var i = nodes.length - 1; i >= 0; i = i - 1) {\n      retValue[i] = nodes[i];\n    }\n\n    return retValue;\n\n    // This is yields undefined behavior according to the ECMA spec\n\t// since this is returns a NodeList which is a host object.\n\t// This causes a break in IE.\n    //return [].slice.call(document.getElementsByTagName(tagname));\n  }\n\n  JR._lines = { info:[], tip:[], warn:[] };\n  JR._console = ('console' in window && 'log' in console && 'warn' in console && 'info' in console);\n\n  JR.reset = \" margin:0;padding:0;border:0;outline:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align: baseline;color:inherit;line-height:inherit;\";\n\n  function html(str){\n    return str.replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  }\n\n  function dmlink(str, url){\n    return '<a style=\"'+JR.reset+'text-decoration:underline;color:#844\" href=\"'+url+'\">'+html(str)+'</a>';\n  }\n\n  function unique(arr){\n    var hash={}, result=[];\n    for (var i=0, l=arr.length; i<l; ++i)\n      if(!hash.hasOwnProperty(arr[i])){\n        hash[arr[i]]=true;\n        result.push(arr[i]);\n      }\n    return result;\n  }\n\n  JR.saveResults = function() {\n    var hResults = {\n      warn: JR._lines.warn, tip: JR._lines.tip, info: JR._lines.info, statsHTML: JR.statsHTML\n    }, summary = \"\", prognosis = $(\"jr_results_prognosis\");\n\n    if (prognosis) summary = prognosis.innerHTML + \", \";\n\n    summary += document.getElementsByTagName('*').length + ' elements';\n    summary = summary.replace(\"'\", \"\\\\'\");\n    JR.SaveToJdrop(\"DOM Monster\", hResults, JR.Version, summary);\n  };\n\n  JR.SaveToJdrop = function(appname, myDataObj, version, summary) {\n    // create object of parameters to pass to Jdrop\n    var params = { \"appname\": appname,\n      \"title\": document.title,\n      \"version\": version,\n      \"summary\": summary,\n      \"json\": JSON.stringify(myDataObj) };\n\n    // hidden iframe to use as target of form submit\n    var jdropif = document.createElement(\"iframe\");\n    jdropif.style.display = \"none\";\n    jdropif.name = \"jdropiframe\";\n    jdropif.id = \"jdropiframe\";\n    document.body.appendChild(jdropif);\n\n    // form for posting data\n    var jdropform = document.createElement(\"form\");\n    jdropform.method = \"post\";\n    jdropform.action = \"http://jdrop.org/save\";\n    jdropform.target = \"jdropiframe\";\n    jdropform.style.display = \"hidden\";\n\n    // add each param to the form as an input field\n    for (var key in params) {\n      var pInput = document.createElement(\"input\");\n      pInput.setAttribute(\"name\", key);\n      pInput.setAttribute(\"value\", params[key]);\n      jdropform.appendChild(pInput);\n    }\n\n    // submit the form and cleanup\n    document.body.appendChild(jdropform);\n    jdropif.onload = function() { document.body.removeChild(jdropform); document.body.removeChild(jdropif); };\n    jdropif.onerror = function() { document.body.removeChild(jdropform); document.body.removeChild(jdropif); };\n    jdropform.submit();\n  };\n\n  JR.JdropCallback = function(jsonobj) {\n    if (!document.getElementById(\"jr_results_prognosis_container\")) {\n      setTimeout(function() { JR.JdropCallback(jsonobj); }, 500);\n      return;\n    }\n    JR._lines.warn = jsonobj['warn'];\n    JR._lines.tip = jsonobj['tip'];\n    JR._lines.info = jsonobj['info'];\n    JR.statsHTML = jsonobj['statsHTML'];\n    JR.flush();\n  };\n\n  window.__DOMMonsterJdropCallBack = JR.JdropCallback;\n\n  JR.close = function(){\n    var results = $('jr_results_tips');\n    results.parentNode.removeChild(results);\n  };\n\n  JR.flush = function(string){\n    var results = $('jr_results_tips'),\n      html = '<div style=\"'+JR.reset+';margin-left:230px;padding-top:4px\">';\n    function flushArray(array){\n      for(var i=0;i<array.length;i++)\n        html += '<div style=\"'+JR.reset+'margin:0 0 4px 0;padding:4px 0px 0px 0px;font-size:11px\">' + array[i] + '</div>';\n    }\n    var prognosis = $(\"jr_results_prognosis\"),\n      container = $(\"jr_results_prognosis_container\"),\n      warnings = JR._lines.warn.length;\n    if(warnings>0) {\n      if(warnings>2) {\n        container.style.cssText += ';color:#A02523';\n        prognosis.innerHTML = 'whoa, potentially huge issues';\n        $('jr_results_warnings').innerHTML = JR._lines.warn.length + ' warnings indicate app ill-health';\n      } else {\n        container.style.cssText += ';color:#E8871D';\n        prognosis.innerHTML = 'room for improvement';\n        $('jr_results_warnings').innerHTML = JR._lines.warn.length + ' warning' + (warnings==1?'':'s');\n      }\n      $('jr_results_warnings_container').style.cssText += ';display:inline';\n    } else {\n      container.style.cssText += ';color:#40a40F';\n      prognosis.innerHTML = 'yay! you\\'re doing a great job!';\n      $('jr_results_warnings_container').style.cssText += ';display:none';\n    }\n\n    flushArray(JR._lines.warn);\n    flushArray(JR._lines.tip);\n    flushArray(JR._lines.info);\n    html += '</div>';\n    results.innerHTML += html;\n\n    $('jr_stats').innerHTML = JR.statsHTML;\n  };\n\n  JR.log = function(string, hint, type){\n    type = type || 'tip';\n    hint = hint || '';\n    var color = { info: '888', tip:'88f', warn:'efb000' }[type];\n    JR._lines[type].push(\n      '<div style=\"'+JR.reset+'text-transform:uppercase;font-size:10px;border:1px solid #'+color+';width:32px;color:#'+color+';-webkit-border-radius:5px;padding:1px;float:left;text-align:center;margin:-2px 4px 0px 0px\">'+type+'</div> '+\n      '<strong style=\"'+JR.reset+'font-weight:bold\">'+string+'</strong> '+hint);\n  };\n  JR.tip = function(string, hint){ JR.log(string,hint,'tip'); };\n  JR.info = function(string, hint){ JR.log(string,hint,'info'); };\n  JR.warn = function(string, hint){ JR.log(string,hint,'warn'); };\n\n  JR.trace = function(msgs){\n    var formatted = \"\";\n    for(var i=0;i<msgs.length;i++)\n      formatted += html(msgs[i]) + '<br>';\n\n    JR._lines['info'].push(\n      '<div style=\"'+JR.reset+';font-family:monospace;border:1px solid #888;padding:5px\">'+formatted+'</div>'\n    );\n  }\n\n  JR.time = function(scope){\n    JR.time.scope = JR.time.scope || {};\n    if(JR.time.scope[scope]) {\n      var duration = (new Date()).getTime()-JR.time.scope[scope];\n      JR.time.scope[scope] = null;\n      return duration/1000;\n    } else {\n      JR.time.scope[scope] = (new Date()).getTime();\n      return null;\n    }\n  };\n\n  JR.benchmark = function(method, times, scope){\n    var i = times || 1000;\n    JR.time(scope||'benchmark');\n    while(i--) method();\n    return JR.time(scope||'benchmark')/times;\n  };\n\n  JR.scriptTagsTips = function(){\n    var nodes = $tagname('script'),\n      head = document.head || $tagname('head')[0];\n\n    var count = 0, headcount = 0, i = nodes.length, sources = [], longInlines = [];\n    while(i--){\n      if(nodes[i].src && nodes[i].src !== ''){\n        if(nodes[i].src.indexOf('dommonster.js') === -1 &&\n           nodes[i].src.indexOf('google-analytics.com/ga.js') === -1 &&\n           nodes[i].src.indexOf('getclicky.com/') === -1){\n          if(nodes[i].parentNode === head){\n             headcount = headcount + 1;\n             sources.push(nodes[i].src);\n          }\n      if(nodes[i].innerHTML.length >= (1024*2))\n            longInlines.push(nodes[i]);\n\n          count = count + 1;\n        }\n      }else{\n        if(nodes[i].parentNode === head){\n          headcount = headcount + 1;\n        }\n\n    if(nodes[i].innerHTML.length >= (1024*2))\n            longInlines.push(nodes[i]);\n\n        count = count + 1;\n      }\n    }\n\n    if(count>2 && count<6)\n      JR.tip('Found '+count+' &lt;script&gt; tags on page.','Try to reduce the number of script tags.');\n    if(count.length>=6)\n      JR.warn('Found '+count+' &lt;script&gt; tags on page.','Try to reduce the number of script tags.');\n\n\n  // inline scripts block rendering\n    // http://www.stevesouders.com/blog/2009/05/06/positioning-inline-scripts/\n    if(longInlines.length>0)\n      JR.tip('Found ' + longInlines.length + ' big (>=2kB) inline script'+((longInlines.length>1)?'s':'')+'.', 'Try to avoid big inline scripts, they block rendering and won\\'t get cached.');\n\n    JR.noLoaders = function() {\n      var loaders = ['head', 'yepnope', '$LAB', 'jsl', 'JSLoader'], r = true;\n      for (var i=0;i<loaders.length;i++)\n        if (loaders[i] in window) {\n          JR.tip(\"You're using \"+loaders[i]+\", a JavaScript loader library.\",\n            \"Often you can achieve the best performance and compression by serving a single \"+\n            \"concatenated JavaScript file instead; however, your page might be more responsive with \"+\n            \"a loader library.\");\n          r = false;\n        }\n      return r;\n    }\n\n    if(headcount>0 && JR.noLoaders())\n      JR.tip('<span style=\"cursor:help\" title=\"'+sources.join('\\n')+'\">Found '+headcount+' &lt;script&gt; tags in HEAD.</span>','For better perceived loading performance move script tags to end of document; or use a non-blocking JS loader library.');\n  };\n\n  function digitCompare(user, edge) {\n    return (~~user || 0) >= (edge || 0);\n  }\n\n  JR.versionCompare = function(userVersion, edgeVersion) {\n    if(userVersion === undefined) return true;\n\n    userVersion = userVersion.split('.');\n\n    var major = digitCompare(userVersion[0], edgeVersion[0]),\n        minor = digitCompare(userVersion[1], edgeVersion[1]),\n        build = digitCompare(userVersion[2], edgeVersion[2]);\n\n    return (!major || major && !minor || major && minor && !build);\n  };\n\n  JR.frameworkTips = function(){\n    // Version number on http://prototypejs.org/download\n    if('Prototype' in window && JR.versionCompare(Prototype.Version, [1, 7, 1]))\n      JR.tip(\"You are using the Prototype JavaScript framework v\"+Prototype.Version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://script.aculo.us/downloads\n    if('Scriptaculous' in window && JR.versionCompare(Scriptaculous.Version, [1, 9, 0]))\n      JR.tip(\"You are using script.aculo.us v\"+Scriptaculous.Version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://jquery.com/\n    if(typeof jQuery == 'function'){\n      if(JR.versionCompare(jQuery.prototype.jquery, [2, 0, 3])) {\n        JR.tip(\"You are using the jQuery JavaScript framework v\"+jQuery.prototype.jquery+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n      }\n      // Version number on http://jqueryui.com/home\n      if(jQuery.ui && JR.versionCompare(jQuery.ui.version, [1, 10, 3])) {\n        JR.tip(\"You are using the jQuery UI JavaScript framework v\"+jQuery.ui.version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n      }\n    }\n\n    // Version number on http://download.dojotoolkit.org/\n    if(typeof dojo == 'object' && JR.versionCompare(dojo.version.toString(), [1, 9, 1]) && !(dojo.version.toString().match(/dev/)))\n      JR.tip(\"You are using the dojo JavaScript toolkit v\"+dojo.version.toString()+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://developer.yahoo.com/yui/\n    if(typeof YAHOO == 'object' && typeof YAHOO.evn == 'object' && JR.versionCompare(YAHOO.env.getVersion('yahoo').version, [2, 8, 2]))\n      JR.tip(\"You are using the Yahoo! User Interface Library 2 v\"+YAHOO.env.getVersion('yahoo').version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://developer.yahoo.com/yui/3/\n    if('YUI' in window && typeof YUI == 'function' && JR.versionCompare(YUI().version, [3, 13, 0]))\n      JR.tip(\"You are using the Yahoo! User Interface Library 3 v\"+YUI().version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://mootools.net/download\n    if(typeof MooTools == 'object' && (!MooTools.version || JR.versionCompare(MooTools.version, [1, 4, 5])))\n      JR.tip(\"You are using the MooTools JavaScript tools v\"+MooTools.version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number Extjs on http://www.sencha.com/products/js/download.php\n    if(typeof Ext === 'object' && JR.versionCompare(Ext.version, [4, 2, 1]))\n      JR.tip(\"You are using the Ext JS v\"+Ext.version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n\n    // Version number on http://rightjs.org/\n    if('RightJS' in window && JR.versionCompare(RightJS.version, [2, 3, 1]))\n      JR.tip(\"You are using the RightJS JavaScript framework v\"+RightJS.version+\".\",\"There's a newer version available, which potentially includes performance updates.\");\n  };\n\n  JR.webfontTips = function(){\n    var tiptext = \"Using external webfont services can increase your page load times, as well as possible downtime if the service goes down.\";\n\n    if(typeof Typekit == 'object')\n      JR.tip(\"You are using the Typekit webfont service.\", tiptext);\n\n    function isFontService(href){\n      return /(webtype|fontdeck|fontslive|fonts|fonts\\.googleapis|kernest|typotheque)\\.com/.test(href)\n    }\n    var styles = document.styleSheets, i = styles.length;\n    if(i==0) return;\n    while(i--) {\n      var href = styles[i].href||'', j = 0;\n      if(styles[i].rules) j = styles[i].rules.length;\n      if(isFontService(href)){\n        if(JR._console) console.log(href);\n        JR.tip(\"You are using an external webfont service.\", tiptext);\n        return \"\";\n      }\n      if(j==0) continue;\n      while(j--){\n        var href = styles[i].rules[j].href||'';\n        if(isFontService(href)){\n          if(JR._console) console.log(href);\n          JR.tip(\"You are using an external webfont service.\", tiptext);\n          return \"\";\n        }\n      }\n    }\n  };\n\n  JR.iFrameTips = function(){\n    var nodes = $tagname('iframe');\n    if(nodes.length>0 && nodes.length<4)\n      JR.tip('Reduce the number of &lt;iframe&gt; tags.','There are '+nodes.length+' iframe elements on the page.');\n    if(nodes.length>=4)\n      JR.warn('Reduce the number of &lt;iframe&gt; tags','There are '+nodes.length+' iframe elements on the page.');\n  };\n\n  JR.cssTips = function(){\n    function linkTagTips(){\n    var nodes = [], links = $tagname('link'), i = links.length;\n    if(i==0) return;\n    while(i--) if((links[i].rel||'').toLowerCase()=='stylesheet') nodes.push(links[i]);\n    if(nodes.length>1 && nodes.length<8)\n      JR.tip('Reduce the number of &lt;link rel=\"stylesheet\"&gt; tags.','There are '+nodes.length+' external stylesheets loaded on the page.');\n    if(nodes.length>=8)\n      JR.warn('Reduce the number of &lt;link rel=\"stylesheet\"&gt; tags','There are '+nodes.length+' external stylesheets loaded on the page.');\n    }\n    function styleAttributeTips(){\n      var nodes = $tagname('*'), i = nodes.length, styleNodes = 0, styleBytes = 0;\n      while(i--)\n        if(nodes[i].style.cssText.length > 0){\n          if(JR._console) console.warn('Inline style', nodes[i]);\n          styleNodes++;\n          styleBytes += nodes[i].style.cssText.length + 8;\n        }\n      if(styleNodes>0)\n        JR.tip('Reduce the number of tags that use the style attribute, replacing it with external CSS definitions.', styleNodes+' nodes use the style attribute, for a total of '+styleBytes+' bytes.');\n    }\n    function dontAtImport() {\n      var styles = $tagname('style'),\n        i = styles.length,\n        present = false;\n      if (i == 0) return;\n      while (i--) if (styles[i].innerHTML.indexOf('@import') != '-1') present = true;\n      if (present)\n        JR.tip('Using @import in a style element will impact rendering performance.', 'Use the &lt;link&gt; tag instead. See '+dmlink('this article', 'http://www.stevesouders.com/blog/2009/04/09/dont-use-import/')+' for details.');\n    }\n    function checkForShadows() {\n      var stylesheets = [].slice(document.styleSheets), shadowCount = 0;\n\n      for (var i = 0; i < stylesheets.length; i++)\n        if (stylesheets[i].cssRules)\n          for (var x = 0; x < stylesheets[i].cssRules.length; x++)\n            if (stylesheets[i].cssRules[x].cssText.indexOf('box-shadow') != '-1') shadowCount++;\n\n      if (shadowCount > 0)\n        JR.tip('Using the box-shadow property can introduce serious scroll & resize lag in the browser.', 'Consider replacing with border-image or reducing the number of elements with shadows (currently: ' + shadowCount + ')');\n    }\n    linkTagTips();\n    styleAttributeTips();\n    dontAtImport();\n    checkForShadows();\n  };\n\n  // via https://gist.github.com/773044\n  function getDocType() {\n    var node = document.firstChild;\n    while (node) {\n      var nodeType = node.nodeType;\n      if (nodeType === 10) {\n        // doctype\n        var doctype = '<!DOCTYPE '+(document.documentElement.tagName || 'html').toLowerCase();\n        if (node.publicId) {\n          doctype += ' PUBLIC \"' + node.publicId + '\"';\n        }\n      if (node.systemId) {\n        doctype += ' \"' + node.systemId + '\"';\n      }\n      return doctype+'>';\n      }\n      if (nodeType === 8 && (\"\"+node.nodeValue).toLowerCase().indexOf(\"doctype\") === 0) {\n        // IE represents DocType as comment\n        return '<!' + node.nodeValue + '>';\n      }\n      node = node.nextSibling;\n    }\n    return \"\";\n  }\n\n  JR.doctypeTips = function(){\n    var dt = getDocType();\n    if(dt !== \"\" && getDocType().toLowerCase() !== '<!doctype html>')\n      JR.tip('Switch to HTML5 and use a short doctype declaration.',\n        html('Using (<!DOCTYPE html>) saves some bytes and increases parsing speed '+\n          '(your current declaration is ' + dt + ').'));\n  };\n\n   JR.flashTips = function() {\n     var nodes = [],\n     obj = $tagname('embed'),\n     i = obj.length;\n\n     if (i) {\n       while (i--) {\n         if ((obj[i].type || '').toLowerCase() == 'application/x-shockwave-flash') nodes.push(obj[i]);\n       }\n     }\n\n     obj = $tagname('object');\n     i = obj.length;\n     if (i) {\n       while (i--) {\n         if ((obj[i].classid || '').toLowerCase() == 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' || (obj[i].type || '').toLowerCase() == 'application/x-shockwave-flash') nodes.push(obj[i]);\n       }\n     }\n\n     if (nodes.length == 1) {\n       JR.tip('Consider alternatives to using Flash.', 'There is 1 Flash object embedded. Replacing this with browser-native implementations (SVG, VML, Canvas) could lead to better loading times, especially if the Flash plugin is loaded first.');\n     } else if (nodes.length) {\n       JR.tip('Consider alternatives to using Flash.', 'There are ' + nodes.length + ' Flash objects embedded. Replacing these with browser-native implementations (SVG, VML, Canvas) could lead to better loading times, especially if the Flash plugin is loaded first.');\n     }\n   };\n\n  JR.getStyle = function(element, style) {\n    style = style == 'float' ? 'cssFloat' : style;\n    var value = element.style[style];\n    if (!value || value == 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n    if (style == 'opacity') return value ? parseFloat(value) : 1.0;\n    return value == 'auto' ? null : value;\n  },\n\n  JR.opacityTips = function(){\n    var nodes = $tagname('*'), op = [], i = nodes.length;\n    while(i--){\n      var opacity = JR.getStyle(nodes[i],'opacity') || 1;\n      if(opacity<1) {\n        nodes[i].style.cssText += ';border:1px dashed #00f';\n        op.push(nodes[i]);\n        if(JR._console) console.info('Transparent node', nodes[i]);\n      }\n    }\n    if(op.length>0 && op.length < 10)\n      JR.tip('Some nodes use transparency.','To improve rendering performance, try not to use the CSS opacity property (found '+op.length+' nodes, marked with a dashed blue border).');\n    if(op.length >= 10)\n      JR.warn('Lots of nodes use transparency.','To improve rendering performance, try not to use the CSS opacity property (found '+op.length+' nodes, marked with a dashed blue border).');\n  };\n\n  JR.nodesTips = function(){\n    function level(value,mid,high){\n      return value<mid?'low':value<high?'mid':'high';\n    }\n    function revlevel(value,mid,high){\n      return value<mid?'high':value<high?'mid':'low';\n    }\n    var nodes = $tagname('*'), i = nodes.length, nodecount = 0, ids = {}, multiIds = [], multiIdsElements = [],\n      empty = 0, deprecated = 0, whitespace = 0, textnodes = 0, comments = 0, deprecatedTags = {}, emptyAttr = 0,\n      js_byte = 0, js = 0, textnodeLength = 0,\n      inlinejs = ['mouseover', 'mouseout','mousedown', 'mouseup','click','dblclick','mousemove', 'load','error','beforeunload','focus','blur','touchstart','touchend','touchmove'];\n\n    var DEPRECATED = (\"font center strike u dir applet acronym bgsound isindex layer ilayer nolayer listing marquee nobr \" +\n      \"noembed plaintext spacer xml xmp\").split(' ');\n\n    var INVALID_FOR_TEXTNODES = (\"script style\").split(' ');\n\n    while(i--) {\n      var tag = nodes[i].tagName.toLowerCase(), attribute, j = inlinejs.length;\n      if (nodes[i].childNodes.length==0 && !(tag=='link' || tag=='br' || tag=='script' || tag=='meta' || tag=='img' ||\n            tag=='a' || tag=='input' || tag=='hr' || tag=='param' || tag=='iframe' ||\n            tag=='area' || tag=='base') && !((nodes[i].id||'') == '_firebugConsole')) {\n        if(JR._console) console.warn('Empty node', nodes[i]);\n        empty++;\n      }\n\n      if(DEPRECATED.indexOf(tag)>-1) {\n        if(JR._console) console.warn('Deprecated node', nodes[i]);\n        if(!deprecatedTags[tag]) deprecatedTags[tag] = true;\n        deprecated++;\n      }\n\n      if(nodes[i].id)\n        if(ids[nodes[i].id]){\n          multiIds.push(nodes[i].id);\n          multiIdsElements.push(nodes[i]);\n        }\n        else ids[nodes[i].id] = true;\n\n      if(tag=='link' && /stylesheet|icon|shortcut|prefetch/.test(nodes[i].rel) && nodes[i].getAttribute('href') === ''){\n        if(JR._console) console.warn('Empty href attribute', nodes[i]);\n        emptyAttr++;\n      }\n\n      if(tag=='html'){\n        attribute = nodes[i].attributes.getNamedItem('manifest');\n        if(attribute && attribute.value === ''){\n          if(JR._console) console.warn('Empty manifest attribute', nodes[i]);\n          emptyAttr++;\n        }\n      }\n\n      if(tag=='video' || tag=='audio' || tag=='iframe' || tag=='input' || tag=='embed' || tag == 'img'){\n        attribute = nodes[i].attributes.getNamedItem('src');\n        if(attribute && attribute.value === '' ){\n          if(JR._console) console.warn('Empty src attribute', nodes[i]);\n          emptyAttr++;\n        }\n      }\n\n      while( j-- ){\n        attribute = nodes[i].getAttribute('on'+inlinejs[j]);\n        if(attribute){\n            if(JR._console) console.warn('Inline JavaScript', nodes[i]);\n            js_byte += 5 + attribute.length + inlinejs[j].length;\n            js++;\n        }\n      }\n\n      // additional toLowerCase check prevents SVG problem, #29\n      if(nodes[i].href && nodes[i].href.toLowerCase && nodes[i].href.toLowerCase().indexOf( \"javascript:\" ) == 0 ){\n        if(JR._console) console.warn('Inline JavaScript', nodes[i]);\n        js++;\n        js_byte += nodes[i].href.length;\n      }\n    }\n    function findWhitespaceTextnodes(element){\n      // Safety check\n      if(element.childNodes && element.childNodes.length>0)\n        for(var i=0;i<element.childNodes.length;i++)\n          findWhitespaceTextnodes(element.childNodes[i]);\n      nodecount++;\n      if(element.nodeType==8)\n        comments++;\n      if(element.nodeType==3 && /^\\s+$/.test(element.nodeValue)){\n        // if(JR._console) console.warn('Whitespace-only text node', element);\n        whitespace++;\n      }\n      if(element.nodeType==3 && INVALID_FOR_TEXTNODES.indexOf(element.parentNode.tagName.toLowerCase())==-1){\n        textnodes++;\n        textnodeLength += element.nodeValue.length;\n      }\n    }\n    findWhitespaceTextnodes(document);\n\n    var contentPercent = textnodeLength/document.body.innerHTML.length*100\n\n    JR.stats(nodecount, 'nodes', level(nodecount,1500,3000));\n    JR.stats(textnodes, 'text nodes', level(textnodes,750,1500));\n    JR.stats((textnodeLength/1024).toFixed(1)+'k', 'text node size', level(textnodeLength,80000,500000));\n    JR.stats(contentPercent.toFixed(2)+'%', 'content percentage', revlevel(contentPercent, 25, 50));\n\n    if(empty) JR.tip('There are '+empty+' empty nodes.','Removing them might improve performance.');\n    if(deprecated) {\n      var tags = [];\n      for(tag in deprecatedTags) tags.push(tag.toUpperCase());\n      JR.tip('There are '+deprecated+' nodes which use a deprecated tag name ('+tags.join(', ')+').','Try updating this content to HTML5.');\n    }\n    if(multiIds.length > 0){\n      JR.warn('There '+((multiIds.length==1)?'is ':'are ')+multiIds.length+' duplicate id'+((multiIds.length>1)?'s':'')+' for nodes in your document.', 'Node ids must be unique within the HTML document. See JavaScript console for details.');\n      if(JR._console) console.warn('Duplicate element ids found', unique(multiIds));\n      if(JR._console) console.warn('Nodes affected by duplicate ids', multiIdsElements);\n    }\n    if(whitespace)\n      JR.tip(((whitespace/nodecount)*100).toFixed(1)+'% of nodes are whitespace-only text nodes.','Reducing the amount of whitespace, like line breaks and tab stops, can help improve the loading and DOM API performance of the page.');\n    if(comments)\n      JR.tip('There are '+comments+' HTML comments.','Removing the comments can help improve the loading and DOM API performance of the page.');\n    if(emptyAttr)\n      JR.warn('There are '+emptyAttr+' HTML elements with empty source attributes.', 'Removing these nodes or updating the attributes will prevent double-loading of the page in some browsers. See this article for more information: '+dmlink('Empty image src can destroy your site','http://www.nczonline.net/blog/2009/11/30/empty-image-src-can-destroy-your-site/'))\n    if(js&&js_byte)\n      JR.tip('There are '+js_byte+' bytes of inline JavaScript code in '+js+' HTML nodes.', 'Removing the inline JavaScript, or updating the attributes will improve the loading speed of the page.');\n  };\n\n  JR.statsHTML = '';\n  JR.stats = function(value, stat, type){\n    var color = { low: '80E41F', mid: 'E8871D', high: 'A02523' };\n    JR.statsHTML +=\n      '<div style=\"'+JR.reset+'margin:0;margin-left:5px;padding:0;margin-bottom:4px;height:auto\"><div style=\"'+JR.reset+';float:left;width:13px;height:13px;margin-right:2px;background:#'+color[type||'low']+'\"> </div>'+\n      '<strong>'+value+'</strong> '+stat+\n      '</div>'\n  };\n\n  JR.globals = function(){\n    function ignore(name){\n      var allowed = ['Components','XPCNativeWrapper','XPCSafeJSObjectWrapper','getInterface','netscape','GetWeakReference'],\n      i = allowed.length;\n      while(i--){\n        if(allowed[i] === name)\n          return true;\n      }\n      return false;\n    }\n\n    function nametag(attr){\n      var ele = nametag.cache = nametag.cache || $tagname('*'), i = ele.length;\n      while(i--){\n        if(ele[i].name && ele[i].name == attr)\n          return true;\n      }\n      return false;\n    }\n\n    var global = (function(){ return this })(), properties = {}, prop, found = [], clean, iframe = document.createElement('iframe');\n    iframe.style.display = 'none';\n    iframe.src = 'about:blank';\n    document.body.appendChild(iframe);\n\n    clean = iframe.contentWindow;\n\n    for(prop in global){\n      if(!ignore(prop) && !/^[0-9]/.test(prop) && !(document.getElementById(prop) || {}).nodeName && !nametag(prop)){\n        properties[prop] = true;\n      }\n    }\n\n    for(prop in clean){\n      if(properties[prop]){\n        delete properties[prop];\n      }\n    }\n\n    for(prop in properties){\n      found.push(prop.split('(')[0]);\n    }\n\n    if(found.length > 5){\n      JR.tip('Found '+found.length+' JavaScript globals.','Cutting back on globals can increase JavaScript performance.' + (JR._console ? ' See JavaScript console for details.' : ''));\n      if(JR._console) console.log('Found more than 5 globals on your page.', found);\n    }\n  };\n\n  JR.performanceTips = function(){\n    var domsize = document.body.innerHTML.length;\n\n    function level(value,mid,high){\n      return value<mid?'low':value<high?'mid':'high';\n    }\n    function parentNodes(node){\n      var counter = 0;\n      if(node.parentNode)\n        while(node = node.parentNode){ counter++ };\n      return counter;\n    }\n    var nodes = $tagname('*'), nodeStats = [], i = nodes.length, average = 0, very = false;\n    while(i--) {\n      average += parentNodes(nodes[i]);\n      if(parentNodes(nodes[i])>15){\n        very = true;\n        nodes[i].style.cssText += ';border:1px dashed #f00';\n      }\n    }\n    average = average/nodes.length;\n\n    JR.stats(nodes.length, 'elements', level(nodes.length,750,1500));\n\n    JR.nodesTips();\n    JR.stats(average.toFixed(1), 'average nesting depth', level(average,8,15));\n    JR.stats((domsize/1024).toFixed(1)+'k', 'serialized DOM size', level(domsize,100*1024,250*1024));\n\n    if(domsize>(100*1024) && domsize<(250*1024))\n      JR.tip('Your serialized DOM size is a little high.','Performance might improve if you reduce the amount of HTML.');\n    if(domsize>=(250*1024))\n      JR.warn('DOM size is higher than 250k.','Performance might improve if you reduce the amount of HTML.');\n\n    var bodycount = JR.benchmark(function(){\n      document.body.appendChild(document.createTextNode(' '));\n      var x = document.body.innerHTML;\n    }, 10);\n    JR.stats(bodycount.toFixed(3)+'s', 'serialization time', level(bodycount,0.05,0.1));\n    if(bodycount>0.1)\n      JR.warn('Average DOM serialization speed is '+bodycount.toFixed(3)+'s.','Try to reduce the complexity of the DOM structure.');\n\n    if(nodes.length>1500)\n      JR.warn('Element count seems excessively high.','Performance might improve if you reduce the number of nodes.');\n    if(average>8 && average<=15)\n      JR.tip('Nesting depth is a little high.','Reducing it might increase performance.');\n    if(very)\n      JR.warn('Nesting depth is very high.','Some of the nodes are nested more than 15 levels deep (these are marked with a dashed red border).');\n\n    JR.cssTips();\n    JR.doctypeTips();\n    JR.frameworkTips();\n    JR.webfontTips();\n    JR.scriptTagsTips();\n    JR.iFrameTips();\n    JR.opacityTips();\n    JR.flashTips();\n    JR.globals();\n\n    if(JR._lines.tip.length == 0 && JR._lines.warn.length == 0)\n      JR.tip('No tips! Congratulations! It seems your site is up to speed!');\n  };\n\n  var IE = !!(window.attachEvent && navigator.userAgent.indexOf('Opera') === -1);\n  if(IE) JR.getStyle = function(element, style) {\n    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style;\n    var value = element.style[style];\n    if (!value && element.currentStyle) value = element.currentStyle[style];\n    if (style == 'opacity') {\n      if (value = (element.style.filter || '').match(/alpha\\(opacity=(.*)\\)/))\n        if (value[1]) return parseFloat(value[1]) / 100;\n      return 1.0;\n    }\n    return value;\n  };\n\n  var old = $('jr_results_tips');\n  if(old) old.parentNode.removeChild(old);\n  setTimeout(function(){\n    var error;\n\n    if(JR._console)\n      JR.info('Check the JavaScript console of your browser for detailed information on some of the tips.');\n    try {\n      if(\"undefined\" === typeof(JDROPVIEW)) JR.performanceTips();\n    } catch(e) {\n      error = e;\n    }\n    if(error){\n      JR.info('Error while analyzing page. '+\n        dmlink('Please let the DOM Monster know about this problem', 'https://github.com/madrobby/dom-monster/issues') + '!',\n        '(Please include the information below)');\n\n      JR.trace(\n        [\n          'Location: '+location.href,\n          'User agent: '+navigator.userAgent\n        ].concat(printStackTrace({e:error})));\n    }\n    var body = $tagname('body')[0], node = document.createElement('div');\n    node.id = 'jr_results';\n    body.appendChild(node);\n\n    JR.statsHTML +=\n      '<a href=\"http://twitter.com/dom_monster\" target=\"_new\" style=\"'+JR.reset+';display:block;margin-top:20px;margin-right:10px;line-height:1.3;padding:6px 6px 6px 48px;background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAF7ElEQVR42q2WaUxUVxTHX2tSK5uyaQS0WIEZARGZDWZAoIhSQQRZBkYZ0FFBXLCmiGyxllhFwKgoAkWpWgVbUQHRurbVtvGDqYnUrRXbNF1iNdbUxqQm9PR%2Fn2%2FizZPCiHz4Zd65b949%2F3uWe69wNCf0RVCDfLADXAA94D54AO6BG%2BAgyAaOtsxpq2M9OAN6AdnIt8BnKASkgieABkE3cHwZAW7gD0CMYwvCxF%2BZLUf%2BvuJlBFgAMdqyddQ4W0H18b7UkODHgM1%2Bn6c%2BwZfq3vah5rkBTMRtfD9isAKaACaBALOWtk33psoID6qK9KIaUD3NkzbDZlRHegIvPHtSTZQX1c6YQPvSgqxR0A9WwAFAVhFHEIVd8UrKVThQrp89VRg86aAxmA6kTaEilTst9rWj5QEjqSVTxRzzKZo3WAGNspxCRCitM3hRIRw2p06hzkXhdHyRgWpnKejdEHfaMsOHOhbq5fWQ88ICsGJGAZsMPBMBmN2%2BMIxhHRefuf%2FKyR5QAHIMdOIzQi0chv3JfI12W9xEqotXyCdEJLR0OEtNR8xaydaJNsb7EhDXn4BXENrqr96ZeeVsfvRO2A7MeXNKoNA4Z5JbsdrlQbnOnTAm5VRHbVkaOrn0Lbq83kxXK%2FOoe%2FNS6q5cCjubjudGsYLlnf8LlMC6OJdjOWFb8LwCjGArX31%2BeQzd2ZBJt9anIZ%2FhteyPh%2BaphAMZU199Tz%2FmVkOikjotBuZcnPRSuYnu7Cqiq5ty6fzq2XRqWSydWxVP31Xl0%2B0dhRAXw0fiF2AnRhmRPZMfvbW7fC51LQ4n2OVssOlUXiTdhPNr61JQVIaeIzmhr2FcTEVNrHdnmdaN9qcH01FMerEoRXTyZWESQq6hNoZZTIfosBsR%2BboknY1bBXQBAYhpvVQ460oPFntiSQQh5c3sRRAq%2B6%2BuxRFiNcPp4%2FYFYeM%2BNgYLVTHjhYrwsc0b0deoh6cFiKrvsIRbHfCwb%2FvaITdy4R9xcsm0388ti8Z%2FxXdbrMUQi5d3uQnUrUhBY5K%2FUBHhsbcyepxU8ZIT0GnRU6dY9egGySEiwcIqF2bkis4J39%2FjamQeX5EK8ANftXAqfDjH%2F%2BJ2bKt8G36apaWdcwLpkBm2JYK6ciPp85WxdLkoQQytdSHgEfDgfEzmhJ0GdvK2iAMEyiXbASJ%2Ba%2Bd6m%2FV5EzagdD9Hmj9pJM33H0Um5UhaMnU07c7Q0NWyJDr2zMk3svlL5a3ZV29eBzfBMBABiMFvyeex2hWasZShcIJjd%2FpgpoIylU6UMtGeWs1hdJJF4en%2FC7h5DeAxIFDX305oBAR%2BAr%2FKi4y143V0S4HOg1LhcFOckq6UJFEWImFEVMoiJ9AJtJj0TQB3p3gojX0Ghg%2B0FaeAHwHxsAI7mx9Fl4sTKTvAmdJ87OmoZRp9sSqOMuDcCDbE%2BlIXukn65pIUUQK9YBN43dbDyBlck4voQASq4v3JhPBngtYcPW1NDKR0XwexFlCY1q74G7SBWpAH%2FAZzGu7jnbPW2z7bn5LftGPOrasWc29GRPZkqOl0XqS1C9YMxaW0iRfAVrY%2FI4Tqk4Nob6aG9hhVVJc8hepTguk4WvHCyul8C8YOhYAWQHIRiISYZ1S7RDhrT34T6gWThkJADaBBcA4ItiBg0%2BgPF9COjYidE1jl87diIHd%2BH6hsFoCc9QuOYNwNJus3RHk%2F%2FCgt%2BAZCzTrjAXbIR9hs%2Fjls1vUiLU%2BkbbcN%2BAPBVthp1y%2FvG8YKJWpXc0GQE5WFjjaxIxXRcGs1qezL9R4NazSjf95vDPbDBjWGXW6A8CIIa9UuA7G6ROPaW6p1pbUq54TK6PFCfaJSwC3JEXYPuFuqcXt9d3KAgFSJICJ90bcATN4fZkASj8F4JqooxFkoVru2SeP3cG0btVblIlRP9xbYPaIlM0Q4KKPFpBJP1w6LnmdAAWs4AQ3ceCA3fh0MA0KRShQmlGrd%2BqQq5g2hLkEh7Iz3szKggOFSFEzsmRv3AN%2BDP0Gq%2FLvi%2FwHpEqPH8x%2FFZLYKQrropQAAAABJRU5ErkJggg==) no-repeat 5px 5px #ddf8ff;border:2px solid #00ceff;border-radius:4px;color:#42c2e0;text-decoration:none\">For daily tips, follow @dom_monster on Twitter!</a>';\n\n    node.style.cssText =\n      JR.reset+'text-align:left;z-index:1000000;letter-spacing:0;position:fixed;bottom:0;'+\n      'color:#444;font:12px/13px \\'Helvetica Neue\\', Verdana, Arial, sans serif;'+\n      'width:80%;left:10%';\n    node.innerHTML =\n      '<div id=\"jr_results_tips\" style=\"'+JR.reset+'max-height:400px;margin:10px;padding:5px;overflow:auto;background:#fff;border:2px solid #b42328;-moz-border-radius:9px;-webkit-border-radius:9px;-webkit-box-shadow: 0px 2px 40px rgba(0,0,0,0.5);-webkit-transition:-webkit-transform ease-out 0.3s;-webkit-transform:translateY(450px)\">' +\n        '<div style=\"'+JR.reset+'height:23px;font-size:16px;font-weight:normal;margin-top:0px;margin-bottom:5px;color:#444\">'+\n          '<div style=\"'+JR.reset+'float:left;padding:5px 0px 3px 5px\" id=\"jr_results_prognosis_container\">'+\n            '<span id=\"jr_results_prognosis\" style=\"'+JR.reset+'\"></span> '+\n            '<span style=\"'+JR.reset+'font-size:12px;font-weight:normal\" id=\"jr_results_warnings_container\"><span id=\"jr_results_warnings\" style=\"'+JR.reset+'\"></span></span>'+\n          '</div>'+\n          '<div style=\"'+JR.reset+'cursor:pointer;float:right;padding:5px 10px 3px 10px;height:15px;background:#b42328;-webkit-border-radius:5px;color:#fff;text-shadow:0px 1px 3px rgba(0,0,0,0.5)\" onclick=\"location.href=\\'//mir.aculo.us/dom-monster/\\'\">'+\n            'dom monster <span style=\"'+JR.reset+'font-size:10px\">v'+JR.Version+'</span>'+\n          '</div>'+\n          '<div style=\"'+JR.reset+'float:right;padding:7px 10px 0px 10px;\">'+\n          '<span style=\"'+JR.reset+'color:#888;font-size:10px;text-decoration:underline;cursor:pointer\" onclick=\"document.getElementById(\\'jr_results_tips\\').saveResults()\">'+\n            'save to Jdrop'+\n          '</span>'+\n          '<span style=\"'+JR.reset+'padding-left:10px;color:#888;font-size:10px;text-decoration:underline;cursor:pointer\" onclick=\"var r=document.getElementById(\\'jr_results_tips\\');r.parentNode.removeChild(r);\">'+\n            'close'+\n          '</span>'+\n          '</div>'+\n        '</div>'+\n        '<div style=\"'+JR.reset+'float:left;width:220px;padding:4px;margin-top:2px\" id=\"jr_stats\">'+\n        '</div>'+\n      '</div>';\n    JR.flush();\n    setTimeout(function(){\n      $('jr_results_tips').style.cssText += ';-webkit-transform:translateY(0px)';\n    }, 10);\n\n    $('jr_results_tips').saveResults = JR.saveResults;\n  },10);\n })();\n\n function printStackTrace(options) {\n     var ex = (options && options.e) ? options.e : null;\n     var guess = options ? !!options.guess : true;\n\n     var p = new printStackTrace.implementation();\n     var result = p.run(ex);\n     return (guess) ? p.guessFunctions(result) : result;\n }\n\n printStackTrace.implementation = function() {};\n\n printStackTrace.implementation.prototype = {\n     run: function(ex) {\n         ex = ex ||\n             (function() {\n                 try {\n                     var _err = __undef__ << 1;\n                 } catch (e) {\n                     return e;\n                 }\n             })();\n         // Use either the stored mode, or resolve it\n         var mode = this._mode || this.mode(ex);\n         if (mode === 'other') {\n             return this.other(arguments.callee);\n         } else {\n             return this[mode](ex);\n         }\n     },\n\n     /**\n      * @return {String} mode of operation for the environment in question.\n      */\n     mode: function(e) {\n         if (e['arguments']) {\n             return (this._mode = 'chrome');\n         } else if (window.opera && e.stacktrace) {\n             return (this._mode = 'opera10');\n         } else if (e.stack) {\n             return (this._mode = 'firefox');\n         } else if (window.opera && !('stacktrace' in e)) { //Opera 9-\n             return (this._mode = 'opera');\n         }\n         return (this._mode = 'other');\n     },\n\n     /**\n      * Given a context, function name, and callback function, overwrite it so that it calls\n      * printStackTrace() first with a callback and then runs the rest of the body.\n      *\n      * @param {Object} context of execution (e.g. window)\n      * @param {String} functionName to instrument\n      * @param {Function} function to call with a stack trace on invocation\n      */\n     instrumentFunction: function(context, functionName, callback) {\n         context = context || window;\n         context['_old' + functionName] = context[functionName];\n         context[functionName] = function() {\n             callback.call(this, printStackTrace());\n             return context['_old' + functionName].apply(this, arguments);\n         };\n         context[functionName]._instrumented = true;\n     },\n\n     /**\n      * Given a context and function name of a function that has been\n      * instrumented, revert the function to it's original (non-instrumented)\n      * state.\n      *\n      * @param {Object} context of execution (e.g. window)\n      * @param {String} functionName to de-instrument\n      */\n     deinstrumentFunction: function(context, functionName) {\n         if (context[functionName].constructor === Function &&\n                 context[functionName]._instrumented &&\n                 context['_old' + functionName].constructor === Function) {\n             context[functionName] = context['_old' + functionName];\n         }\n     },\n\n     /**\n      * Given an Error object, return a formatted Array based on Chrome's stack string.\n      *\n      * @param e - Error object to inspect\n      * @return Array<String> of function calls, files and line numbers\n      */\n     chrome: function(e) {\n         return e.stack.replace(/^[^\\(]+?[\\n$]/gm, '').replace(/^\\s+at\\s+/gm, '').replace(/^Object.<anonymous>\\s*\\(/gm, '{anonymous}()@').split('\\n');\n     },\n\n     /**\n      * Given an Error object, return a formatted Array based on Firefox's stack string.\n      *\n      * @param e - Error object to inspect\n      * @return Array<String> of function calls, files and line numbers\n      */\n     firefox: function(e) {\n         return e.stack.replace(/(?:\\n@:0)?\\s+$/m, '').replace(/^\\(/gm, '{anonymous}(').split('\\n');\n     },\n\n     /**\n      * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.\n      *\n      * @param e - Error object to inspect\n      * @return Array<String> of function calls, files and line numbers\n      */\n     opera10: function(e) {\n         var stack = e.stacktrace;\n         var lines = stack.split('\\n'), ANON = '{anonymous}',\n             lineRE = /.*line (\\d+), column (\\d+) in ((<anonymous function\\:?\\s*(\\S+))|([^\\(]+)\\([^\\)]*\\))(?: in )?(.*)\\s*$/i, i, j, len;\n         for (i = 2, j = 0, len = lines.length; i < len - 2; i++) {\n             if (lineRE.test(lines[i])) {\n                 var location = RegExp.$6 + ':' + RegExp.$1 + ':' + RegExp.$2;\n                 var fnName = RegExp.$3;\n                 fnName = fnName.replace(/<anonymous function\\:?\\s?(\\S+)?>/g, ANON);\n                 lines[j++] = fnName + '@' + location;\n             }\n         }\n\n         lines.splice(j, lines.length - j);\n         return lines;\n     },\n\n     // Opera 7.x-9.x only!\n     opera: function(e) {\n         var lines = e.message.split('\\n'), ANON = '{anonymous}',\n             lineRE = /Line\\s+(\\d+).*script\\s+(http\\S+)(?:.*in\\s+function\\s+(\\S+))?/i,\n             i, j, len;\n\n         for (i = 4, j = 0, len = lines.length; i < len; i += 2) {\n             //TODO: RegExp.exec() would probably be cleaner here\n             if (lineRE.test(lines[i])) {\n                 lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) + ' -- ' + lines[i + 1].replace(/^\\s+/, '');\n             }\n         }\n\n         lines.splice(j, lines.length - j);\n         return lines;\n     },\n\n     // Safari, IE, and others\n     other: function(curr) {\n         var ANON = '{anonymous}', fnRE = /function\\s*([\\w\\-$]+)?\\s*\\(/i,\n             stack = [], fn, args, maxStackSize = 10;\n\n         while (curr && stack.length < maxStackSize) {\n             fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;\n             args = Array.prototype.slice.call(curr['arguments']);\n             stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';\n             curr = curr.caller;\n         }\n         return stack;\n     },\n\n     /**\n      * Given arguments array as a String, subsituting type names for non-string types.\n      *\n      * @param {Arguments} object\n      * @return {Array} of Strings with stringified arguments\n      */\n     stringifyArguments: function(args) {\n         for (var i = 0; i < args.length; ++i) {\n             var arg = args[i];\n             if (arg === undefined) {\n                 args[i] = 'undefined';\n             } else if (arg === null) {\n                 args[i] = 'null';\n             } else if (arg.constructor) {\n                 if (arg.constructor === Array) {\n                     if (arg.length < 3) {\n                         args[i] = '[' + this.stringifyArguments(arg) + ']';\n                     } else {\n                         args[i] = '[' + this.stringifyArguments(Array.prototype.slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(Array.prototype.slice.call(arg, -1)) + ']';\n                     }\n                 } else if (arg.constructor === Object) {\n                     args[i] = '#object';\n                 } else if (arg.constructor === Function) {\n                     args[i] = '#function';\n                 } else if (arg.constructor === String) {\n                     args[i] = '\"' + arg + '\"';\n                 }\n             }\n         }\n         return args.join(',');\n     },\n\n     sourceCache: {},\n\n     /**\n      * @return the text from a given URL.\n      */\n     ajax: function(url) {\n         var req = this.createXMLHTTPObject();\n         if (!req) {\n             return;\n         }\n         req.open('GET', url, false);\n         req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');\n         req.send('');\n         return req.responseText;\n     },\n\n     /**\n      * Try XHR methods in order and store XHR factory.\n      *\n      * @return <Function> XHR function or equivalent\n      */\n     createXMLHTTPObject: function() {\n         var xmlhttp, XMLHttpFactories = [\n             function() {\n                 return new XMLHttpRequest();\n             }, function() {\n                 return new ActiveXObject('Msxml2.XMLHTTP');\n             }, function() {\n                 return new ActiveXObject('Msxml3.XMLHTTP');\n             }, function() {\n                 return new ActiveXObject('Microsoft.XMLHTTP');\n             }\n         ];\n         for (var i = 0; i < XMLHttpFactories.length; i++) {\n             try {\n                 xmlhttp = XMLHttpFactories[i]();\n                 // Use memoization to cache the factory\n                 this.createXMLHTTPObject = XMLHttpFactories[i];\n                 return xmlhttp;\n             } catch (e) {}\n         }\n     },\n\n     /**\n      * Given a URL, check if it is in the same domain (so we can get the source\n      * via Ajax).\n      *\n      * @param url <String> source url\n      * @return False if we need a cross-domain request\n      */\n     isSameDomain: function(url) {\n         return url.indexOf(location.hostname) !== -1;\n     },\n\n     /**\n      * Get source code from given URL if in the same domain.\n      *\n      * @param url <String> JS source URL\n      * @return <Array> Array of source code lines\n      */\n     getSource: function(url) {\n         if (!(url in this.sourceCache)) {\n             this.sourceCache[url] = this.ajax(url).split('\\n');\n         }\n         return this.sourceCache[url];\n     },\n\n     guessFunctions: function(stack) {\n         for (var i = 0; i < stack.length; ++i) {\n             var reStack = /\\{anonymous\\}\\(.*\\)@(\\w+:\\/\\/([\\-\\w\\.]+)+(:\\d+)?[^:]+):(\\d+):?(\\d+)?/;\n             var frame = stack[i], m = reStack.exec(frame);\n             if (m) {\n                 var file = m[1], lineno = m[4]; //m[7] is character position in Chrome\n                 if (file && this.isSameDomain(file) && lineno) {\n                     var functionName = this.guessFunctionName(file, lineno);\n                     stack[i] = frame.replace('{anonymous}', functionName);\n                 }\n             }\n         }\n         return stack;\n     },\n\n     guessFunctionName: function(url, lineNo) {\n         try {\n             return this.guessFunctionNameFromLines(lineNo, this.getSource(url));\n         } catch (e) {\n             return 'getSource failed with url: ' + url + ', exception: ' + e.toString();\n         }\n     },\n\n     guessFunctionNameFromLines: function(lineNo, source) {\n         var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/;\n         var reGuessFunction = /['\"]?([0-9A-Za-z_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/;\n         // Walk backwards from the first line in the function until we find the line which\n         // matches the pattern above, which is the function definition\n         var line = \"\", maxLines = 10;\n         for (var i = 0; i < maxLines; ++i) {\n             line = source[lineNo - i] + line;\n             if (line !== undefined) {\n                 var m = reGuessFunction.exec(line);\n                 if (m && m[1]) {\n                     return m[1];\n                 } else {\n                     m = reFunctionArgNames.exec(line);\n                     if (m && m[1]) {\n                         return m[1];\n                     }\n                 }\n             }\n         }\n         return '(?)';\n     }\n };\n\n /*\n     http://www.JSON.org/json2.js\n     2010-11-17\n     Public Domain.\n     NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n     See http://www.JSON.org/js.html\n */\n if(!this.JSON){this.JSON={}}(function(){function f(n){return n<10?\"0\"+n:n}if(typeof Date.prototype.toJSON!==\"function\"){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+\"-\"+f(this.getUTCMonth()+1)+\"-\"+f(this.getUTCDate())+\"T\"+f(this.getUTCHours())+\":\"+f(this.getUTCMinutes())+\":\"+f(this.getUTCSeconds())+\"Z\":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf()}}var cx=/[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,escapable=/[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,gap,indent,meta={\"\\b\":\"\\\\b\",\"\\t\":\"\\\\t\",\"\\n\":\"\\\\n\",\"\\f\":\"\\\\f\",\"\\r\":\"\\\\r\",'\"':'\\\\\"',\"\\\\\":\"\\\\\\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'\"'+string.replace(escapable,function(a){var c=meta[a];return typeof c===\"string\"?c:\"\\\\u\"+(\"0000\"+a.charCodeAt(0).toString(16)).slice(-4)})+'\"':'\"'+string+'\"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value===\"object\"&&typeof value.toJSON===\"function\"){value=value.toJSON(key)}if(typeof rep===\"function\"){value=rep.call(holder,key,value)}switch(typeof value){case\"string\":return quote(value);case\"number\":return isFinite(value)?String(value):\"null\";case\"boolean\":case\"null\":return String(value);case\"object\":if(!value){return\"null\"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)===\"[object Array]\"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||\"null\"}v=partial.length===0?\"[]\":gap?\"[\\n\"+gap+partial.join(\",\\n\"+gap)+\"\\n\"+mind+\"]\":\"[\"+partial.join(\",\")+\"]\";gap=mind;return v}if(rep&&typeof rep===\"object\"){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k===\"string\"){v=str(k,value);if(v){partial.push(quote(k)+(gap?\": \":\":\")+v)}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?\": \":\":\")+v)}}}}v=partial.length===0?\"{}\":gap?\"{\\n\"+gap+partial.join(\",\\n\"+gap)+\"\\n\"+mind+\"}\":\"{\"+partial.join(\",\")+\"}\";gap=mind;return v}}if(typeof JSON.stringify!==\"function\"){JSON.stringify=function(value,replacer,space){var i;gap=\"\";indent=\"\";if(typeof space===\"number\"){for(i=0;i<space;i+=1){indent+=\" \"}}else{if(typeof space===\"string\"){indent=space}}rep=replacer;if(replacer&&typeof replacer!==\"function\"&&(typeof replacer!==\"object\"||typeof replacer.length!==\"number\")){throw new Error(\"JSON.stringify\")}return str(\"\",{\"\":value})}}if(typeof JSON.parse!==\"function\"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value===\"object\"){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return\"\\\\u\"+(\"0000\"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\\],:{}\\s]*$/.test(text.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g,\"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g,\"\"))){j=eval(\"(\"+text+\")\");return typeof reviver===\"function\"?walk({\"\":j},\"\"):j}throw new SyntaxError(\"JSON.parse\")}}}());\n"
  }
]