text: /* see.js version 0.2
  
  see
  ===
  
  see.js: See and debug local variables.
  
  The see.js debugger can be used to inspect and change local variable
  state when you are not stopped at a breakpoint.  This is a good solution
  for debugging code wrapped in a top-level anonymous closure or for
  debugging more complicated uses of closures.  The see.js debugger
  also provides simple in-page logging with tree view inspection of
  objects; and it provides support for debugging using CoffeeScript.
  
  
  Overview
  --------
  
  Any local scope can be debugged by calling eval(see.here)
  within the scope of interest.  For example, the following nicely
  encapsulated code would normally be painful to debug without the
  added see line:
  
  <pre>
  (function() {
    var private_var = 0;
    function myclosuremaker() {
      <mark>eval(see.here);</mark>  // Debug variables visible in this scope.
      var counter = 0;
      return function() { ++counter; }
    }
    var inc = myclosuremaker();
    inc();
  })();
  </pre>
  
  When eval(see.here) is called, the see debugger is shown at the
  bottom of the page, an en eval hook is set up within the current scope.
  
  The debugging panel works like the Firebug or Chrome debugger
  console, except that it uses the eval hook to give visibility into
  local variable scope.  In this example, "counter" and "private_var"
  and "inc" and "myclosuremaker" will all be visible symbols that
  can be used and manipulated in the debugger.
  
  
  Debugging multiple scopes
  -------------------------
  
  It is possible to attach to multiple scopes with a single program
  by adding the following at the scope of interest:
  
  <pre>
  eval(see.scope('scopename'));
  </pre>
  
  To switch scopes within the interactive panel, just enter ":" followed
  by the scope name, for example, ":scopename".  ":top" goes to global
  scope, and ":" goes back to the default scope defined at init.
  
  ![Screenshot of see panel](see-usage.png?raw=true)
  
  
  Bookmarklet
  -----------
  
  This URL can be used as a bookmarklet that loads see.js on any page.
  
  <pre>
  javascript:(function(){function%20a(a,b){function%20c(a,b){a.onload=a.onreadystatechange=b}var%20d=document.createElement(%22script%22),e=document.getElementsByTagName(%22head%22)[0],f=1;c(d,function(){f&&(!d.readyState||{loaded%3A1,complete%3A1}[d.readyState])&&(f=0,b(),c(d,null),e.removeChild(d))}),d.src=a,e.appendChild(d)}a(%22//raw.github.com/davidbau/see/master/see.js%22,function(){see.init()})})();
  </pre>
  
  When you are using the bookmarklet, eval(see.here) calls
  may not be present in the code, but it is possible to insert the
  see eval loop by evaluating using your regular (Chrome or Firebug)
  debugger to run eval(see.here) when at a breakpoint in the scope
  of interest.
  
  
  CoffeeScript
  ------------
  
  The see.js script originally started as a teaching tool in a
  CoffeeScript environment, so it also supports use of CoffeeScript
  as the console language.  Here it how to initialize see.js to
  interpret code entered in the panel as CoffeeScript instead of
  Javascript:
  
  <pre>
  see.init(eval(see.cs))
  </pre>
  
  
  Logging
  -------
  
  The top-level see function logs output to the see panel.  Logged
  objects are shown in a tree view of the object state at the
  moment when the object is logged.
  
  <pre>
  see(a, b, c);
  </pre>
  
  The panel can be cleared with see.clear().
  
  
  Using the regular debugger
  --------------------------
  
  To inspect an object visible to see in the regular debugger, just
  use see.eval('mylocal'), which evaluates the expression in the scope
  of interest and returns the value.  To focus on a different named
  scope, use the two-argument form, see.eval('scopename', 'myexpression').
  
  
  
  More examples of usage
  ----------------------
  
  <pre>
  see.init();               // Creates the interactive panel with global scope.
  see.init({height: 30, title: 'test panel'});   // Sets some options.
  eval(see.init());         // Does the same thing as eval(see.here).
  eval(see.scope('name'));  // Type ":name" in the panel to use this scope.
  see(a, b, c);             // Logs values into the panel.
  see.loghtml('&lt;b>ok&lt;/b>'); // Logs HTML without escaping.
  r = see.repr(a, 3);       // Builds a tree representation of a to depth 3.
  x = see.noconflict();     // Relinguishes use of the 'see' name; use 'x'.
  </pre>
  
  
  Options to pass to init
  -----------------------
  <table>
  <tr><td>eval</td><td>The default function (or closure) to use to evaluate expressions.</td></tr>
  <tr><td>this</td><td>The object to use as "this" within the evaluation.</td></tr>
  <tr><td>depth</td><td>The depth to which to traverse logged and evaluated objects.</td></tr>
  <tr><td>height</td><td>The pixel height of the interactive panel.</td></tr>
  <tr><td>title</td><td>A title shown at the top of the panel.</td></tr>
  <tr><td>panel</td><td>false if no interactive panel is desired.</td></tr>
  <tr><td>console</td><td>Set to window.console to echo logging to the console also.</td></tr>
  <tr><td>history</td><td>Set to false to disable localStorage use for interactive history.</td></tr>
  <tr><td>linestyle</td><td>CSS style for a single log line.</td></tr>
  <tr><td>element</td><td>(if panel is false) - The element into which to logging is done.</td></tr>
  <tr><td>autoscroll</td><td>(if panel is false) - The element to autoscroll to bottom.</td></tr>
  <tr><td>jQuery</td><td>A local copy of jQuery to reuse instead of loading one.</td></tr>
  <tr><td>coffee</td><td>The CoffeeScript compiler object, for coffeescript support.</td></tr>
  <tr><td>abbreviate</td><td>Array of return values (e.g. undefined) to silence in interaction.</td></tr>
  <tr><td>noconflict</td><td>Name to use instead of "see".</td></tr>
  </table>
  
  
  Implementation notes
  --------------------
  
  If eval(see.init()) or eval(see.scope('name')) is called multiple
  times for the same name, the scope is reset to the last scope set.
  If multiple closures are created at the same line of code, that means that
  you will only see the last one.  You can generate a name using
  eval(see.scope('name' + index)) if you want to preserve visibility
  into many scopes.
  
  When see.init() is called, a private (noconflict) copy of jQuery is
  loaded if window.jQuery is not already present on the page (unless
  the 'panel' option is false, or the 'jQuery' option is explicity
  supplied to init).
  
  Every expression entered in the panel is stored to '_loghistory' in
  localStorage unless the 'history' option set to a different key name, or
  set to false to disable history persistence.
  
  */
  
  (function() {
  var $ = window.jQuery;
  var pulljQueryVersion = "1.9.1";
  
  var seepkg = 'see'; // Defines the global package name used.
  var version = '0.2';
  var oldvalue = noteoldvalue(seepkg);
  // Option defaults
  var linestyle = 'position:relative;display:block;font-family:monospace;' +
    'word-break:break-all;margin-bottom:3px;padding-left:1em;';
  var logdepth = 5;
  var autoscroll = false;
  var logelement = 'body';
  var panel = true;
  var see;  // defined below.
  var paneltitle = '';
  var logconsole = null;
  var uselocalstorage = '_loghistory';
  var panelheight = 100;
  var currentscope = '';
  var scopes = {
    '':  { e: window.eval, t: window },
    top: { e: window.eval, t: window }
  };
  var coffeescript = window.CoffeeScript;
  var seejs = '(function(){return eval(arguments[0]);})';
  
  // If see has already been loaded, then return without doing anything.
  if (window.see && window.see.js && window.see.js == seejs &&
      window.see.version == version) {
    return;
  }
  
  
  function init(options) {
    if (arguments.length === 0) {
      options = {};
    } else if (arguments.length == 2) {
      var newopt = {};
      newopt[arguments[0]] = arguments[1];
      options = newopt;
    } else if (arguments.length == 1 && typeof arguments[0] == 'function') {
      options = {'eval': arguments[0]};
    }
    if (options.hasOwnProperty('jQuery')) { $ = options.jQuery; }
    if (options.hasOwnProperty('eval')) { scopes[''].e = options['eval']; }
    if (options.hasOwnProperty('this')) { scopes[''].t = options['this']; }
    if (options.hasOwnProperty('element')) { logelement = options.element; }
    if (options.hasOwnProperty('autoscroll')) { autoscroll = options.autoscroll; }
    if (options.hasOwnProperty('linestyle')) { linestyle = options.linestyle; }
    if (options.hasOwnProperty('depth')) { logdepth = options.depth; }
    if (options.hasOwnProperty('panel')) { panel = options.panel; }
    if (options.hasOwnProperty('height')) { panelheight = options.height; }
    if (options.hasOwnProperty('title')) { paneltitle = options.title; }
    if (options.hasOwnProperty('console')) { logconsole = options.console; }
    if (options.hasOwnProperty('history')) { uselocalstorage = options.history; }
    if (options.hasOwnProperty('coffee')) { coffeescript = options.coffee; }
    if (options.hasOwnProperty('abbreviate')) { abbreviate = options.abbreviate; }
    if (options.hasOwnProperty('noconflict')) { noconflict(options.noconflict); }
    if (panel) {
      // panel overrides element and autoscroll.
      logelement = '#_testlog';
      autoscroll = '#_testscroll';
      pulljQuery(tryinitpanel);
    }
    return scope();
  }
  
  function scope(name, evalfuncarg, evalthisarg) {
    if (arguments.length <= 1) {
      if (!arguments.length) {
        name = '';
      }
      return seepkg + '.scope(' + cstring(name) + ',' + seejs + ',this)';
    }
    scopes[name] = { e: evalfuncarg, t: evalthisarg };
  }
  
  function seeeval(scope, code) {
    if (arguments.length == 1) {
      code = scope;
      scope = '';
    }
    var ef = scopes[''].e, et = scopes[''].t;
    if (scopes.hasOwnProperty(scope)) {
      if (scopes[scope].e) { ef = scopes[scope].e; }
      if (scopes[scope].t) { et = scopes[scope].t; }
    }
    return ef.call(et, code);
  }
  
  var varpat = '[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*';
  var initialvardecl = new RegExp(
    '^\\s*var\\s+(?:' + varpat + '\\s*,)*' + varpat + '\\s*;\\s*');
  
  function barecs(s) {
    // Compile coffeescript in bare mode.
    var compiler = coffeescript || window.CoffeeScript;
    var compiled = compiler.compile(s, {bare:1});
    if (compiled) {
      // Further strip top-level var decls out of the coffeescript so
      // that assignments can leak out into the enclosing scope.
      compiled = compiled.replace(initialvardecl, '');
    }
    return compiled;
  }
  
  function exportsee() {
    see.repr = repr;
    see.html = loghtml;
    see.noconflict = noconflict;
    see.init = init;
    see.scope = scope;
    see.eval = seeeval;
    see.barecs = barecs;
    see.here = 'eval(' + seepkg + '.init())';
    see.clear = clear;
    see.js = seejs;
    see.cs = '(function(){return eval(' + seepkg + '.barecs(arguments[0]));})';
    see.version = version;
    window[seepkg] = see;
  }
  
  function noteoldvalue(name) {
    return {
      name: name,
      has: window.hasOwnProperty(name),
      value: window[name],
    };
  }
  
  function restoreoldvalue(old) {
    if (!old.has) {
      delete window[old.name];
    } else {
      window[old.name] = old.value;
    }
  }
  
  function noconflict(newname) {
    if (!newname || typeof(newname) != 'string') {
      newname = 'see' + (1 + Math.random() + '').substr(2);
    }
    if (oldvalue) {
      restoreoldvalue(oldvalue);
    }
    seepkg = newname;
    oldvalue = noteoldvalue(newname);
    exportsee();
    return see;
  }
  
  function pulljQuery(callback) {
    if (!pulljQueryVersion || ($ && $.fn && $.fn.jquery)) {
      callback();
      return;
    }
    function loadscript(src, callback) {
      function setonload(script, fn) {
        script.onload = script.onreadystatechange = fn;
      }
      var script = document.createElement("script"),
         head = document.getElementsByTagName("head")[0],
         pending = 1;
      setonload(script, function() {
        if (pending && (!script.readyState ||
            {loaded:1,complete:1}[script.readyState])) {
          pending = 0;
          callback();
          setonload(script, null);
          head.removeChild(script);
        }
      });
      script.src = src;
      head.appendChild(script);
    }
    loadscript(
        '//ajax.googleapis.com/ajax/libs/jquery/' +
        pulljQueryVersion + '/jquery.min.js',
        function() {
      $ = jQuery.noConflict(true);
      callback();
    });
  }
  
  // ---------------------------------------------------------------------
  // LOG FUNCTION SUPPORT
  // ---------------------------------------------------------------------
  var logcss = "input._log:focus{outline:none;}label._log > span:first-of-type:hover{text-decoration:underline;}samp._log > label._log,samp_.log > span > label._log{display:inline-block;vertical-align:top;}label._log > span:first-of-type{margin-left:2em;text-indent:-1em;}label._log > ul{display:none;padding-left:14px;margin:0;}label._log > span:before{content:'';font-size:70%;font-style:normal;display:inline-block;width:0;text-align:center;}label._log > span:first-of-type:before{content:'\\0025B6';}label._log > ul > li{display:block;white-space:pre-line;margin-left:2em;text-indent:-1em}label._log > ul > li > samp{margin-left:-1em;text-indent:0;white-space:pre;}label._log > input[type=checkbox]:checked ~ span{margin-left:2em;text-indent:-1em;}label._log > input[type=checkbox]:checked ~ span:first-of-type:before{content:'\\0025BC';}label._log > input[type=checkbox]:checked ~ span:before{content:'';}label._log,label._log > input[type=checkbox]:checked ~ ul{display:block;}label._log > span:first-of-type,label._log > input[type=checkbox]:checked ~ span{display:inline-block;}label._log > input[type=checkbox],label._log > input[type=checkbox]:checked ~ span > span{display:none;}";
  var addedcss = false;
  var cescapes = {
    '\0': '\\0', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r',
    '\t': '\\t', '\v': '\\v', "'": "\\'", '"': '\\"', '\\': '\\\\'
  };
  var retrying = null;
  var queue = [];
  see = function see() {
    if (logconsole && typeof(logconsole.log) == 'function') {
      logconsole.log.apply(window.console, arguments);
    }
    var args = Array.prototype.slice.call(arguments);
    queue.push('<samp class="_log">');
    while (args.length) {
      var obj = args.shift();
      if (vtype(obj) == 'String')  {
        // Logging a string just outputs the string without quotes.
        queue.push(htmlescape(obj));
      } else {
        queue.push(repr(obj, logdepth, queue));
      }
      if (args.length) { queue.push(' '); }
    }
    queue.push('</samp>');
    flushqueue();
  };
  
  function loghtml(html) {
    queue.push('<samp class="_log">');
    queue.push(html);
    queue.push('</samp>');
    flushqueue();
  }
  
  function vtype(obj) {
    var bracketed = Object.prototype.toString.call(obj);
    var vt = bracketed.substring(8, bracketed.length - 1);
    if (vt == 'Object') {
      if ('length' in obj && 'slice' in obj && 'number' == typeof obj.length) {
        return 'Array';
      }
      if ('originalEvent' in obj && 'target' in obj && 'type' in obj) {
        return vtype(obj.originalEvent);
      }
    }
    return vt;
  }
  
  function isprimitive(vt) {
    switch (vt) {
      case 'String':
      case 'Number':
      case 'Boolean':
      case 'Undefined':
      case 'Date':
      case 'RegExp':
      case 'Null':
        return true;
    }
    return false;
  }
  
  function isdom(obj) {
    return (obj.nodeType && obj.nodeName && typeof(obj.cloneNode) == 'function');
  }
  
  function midtruncate(s, maxlen) {
    if (maxlen && maxlen > 3 && s.length > maxlen) {
      return s.substring(0, Math.floor(maxlen / 2) - 1) + '...' +
          s.substring(s.length - (Math.ceil(maxlen / 2) - 2));
    }
    return s;
  }
  
  function cstring(s, maxlen) {
    function cescape(c) {
      if (cescapes.hasOwnProperty(c)) {
        return cescapes[c];
      }
      var temp = '0' + c.charCodeAt(0).toString(16);
      return '\\x' + temp.substring(temp.length - 2);
    }
    if (s.indexOf('"') == -1 || s.indexOf('\'') != -1) {
      return midtruncate('"' +
          htmlescape(s.replace(/[\0-\x1f\x7f-\x9f"\\]/g, cescape)) + '"', maxlen);
    } else {
      return midtruncate("'" +
          htmlescape(s.replace(/[\0-\x1f\x7f-\x9f'\\]/g, cescape)) + "'", maxlen);
    }
  }
  function tiny(obj, maxlen) {
    var vt = vtype(obj);
    if (vt == 'String') { return cstring(obj, maxlen); }
    if (vt == 'Undefined' || vt == 'Null') { return vt.toLowerCase(); }
    if (isprimitive(vt)) { return '' + obj; }
    if (vt == 'Array' && obj.length === 0) { return '[]'; }
    if (vt == 'Object' && isshort(obj)) { return '{}'; }
    if (isdom(obj) && obj.nodeType == 1) {
      if (obj.hasAttribute('id')) {
        return obj.tagName.toLowerCase() +
            '#' + htmlescape(obj.getAttribute('id'));
      } else {
        if (obj.hasAttribute('class')) {
          var classname = obj.getAttribute('class').split(' ')[0];
          if (classname) {
            return obj.tagName.toLowerCase() + '.' + htmlescape(classname);
          }
        }
        return obj.tagName.toLowerCase();
      }
    }
    return vt;
  }
  function isnonspace(dom) {
    return (dom.nodeType != 3 || /[^\s]/.exec(dom.textContent));
  }
  function trimemptystartline(s) {
    return s.replace(/^\s*\n/, '');
  }
  function isshort(obj, shallow, maxlen) {
    var vt = vtype(obj);
    if (isprimitive(vt)) { return true; }
    if (!shallow && vt == 'Array') { return !maxlen || obj.length <= maxlen; }
    if (isdom(obj)) {
      if (obj.nodeType == 9 || obj.nodeType == 11) return false;
      if (obj.nodeType == 1) {
        return (obj.firstChild === null ||
           obj.firstChild.nextSibling === null &&
           obj.firstChild.nodeType == 3 &&
           obj.firstChild.textContent.length <= maxlen);
      }
      return true;
    }
    if (vt == 'Function') {
      var sc = obj.toString();
      return (sc.length - sc.indexOf('{') <= maxlen);
    }
    if (vt == 'Error') {
      return !!obj.stack;
    }
    var count = 0;
    for (var prop in obj) {
      if (obj.hasOwnProperty(prop)) {
        count += 1;
        if (shallow && !isprimitive(vtype(obj[prop]))) { return false; }
        if (maxlen && count > maxlen) { return false; }
      }
    }
    return true;
  }
  function domsummary(dom, maxlen) {
    var short;
    if ('outerHTML' in dom) {
      short = isshort(dom, true, maxlen);
      var html = dom.cloneNode(short).outerHTML;
      var tail = null;
      if (!short) {
        var m = /^(.*)(<\/[^\s]*>$)/.exec(html);
        if (m) {
          tail = m[2];
          html = m[1];
        }
      }
      return [htmlescape(html), tail && htmlescape(tail)];
    }
    if (dom.nodeType == 1) {
      var parts = ['<' + dom.tagName];
      for (var j = 0; j < dom.attributes.length; ++j) {
        parts.push(domsummary(dom.attributes[j], maxlen)[0]);
      }
      short = isshort(dom, true, maxlen);
      if (short && dom.firstChild) {
        return [htmlescape(parts.join(' ') + '>' +
            dom.firstChild.textContent + '</' + dom.tagName + '>'), null];
      }
      return [htmlescape(parts.join(' ') + (dom.firstChild? '>' : '/>')),
          !dom.firstChild ? null : htmlescape('</' + dom.tagName + '>')];
    }
    if (dom.nodeType == 2) {
      return [htmlescape(dom.name + '="' +
          htmlescape(midtruncate(dom.value, maxlen), '"') + '"'), null];
    }
    if (dom.nodeType == 3) {
      return [htmlescape(trimemptystartline(dom.textContent)), null];
    }
    if (dom.nodeType == 4) {
      return ['<![CDATA[' + htmlescape(midtruncate(dom.textContent, maxlen)) +
          ']]>', null];
    }
    if (dom.nodeType == 8) {
      return ['<!--' + htmlescape(midtruncate(dom.textContent, maxlen)) +
          '-->', null];
    }
    if (dom.nodeType == 10) {
      return ['<!DOCTYPE ' + htmlescape(dom.nodeName) + '>', null];
    }
    return [dom.nodeName, null];
  }
  function summary(obj, maxlen) {
    var vt = vtype(obj);
    if (isprimitive(vt)) {
      return tiny(obj, maxlen);
    }
    if (isdom(obj)) {
      var ds = domsummary(obj, maxlen);
      return ds[0] + (ds[1] ? '...' + ds[1] : '');
    }
    if (vt == 'Function') {
      var ft = obj.toString();
      if (ft.length - ft.indexOf('{') > maxlen) {
        ft = ft.replace(/\{(?:.|\n)*$/, '').trim();
      }
      return ft;
    }
    if ((vt == 'Error' || vt == 'ErrorEvent') && 'message' in obj) {
      return obj.message;
    }
    var pieces = [];
    if (vt == 'Array' && obj.length < maxlen) {
      var identical = (obj.length > 1);
      var firstobj = identical && obj[0];
      for (var j = 0; j < obj.length; ++j) {
        if (identical && obj[j] !== firstobj) { identical = false; }
        pieces.push(tiny(obj[j], maxlen));
      }
      if (identical) {
        return '[' + tiny(firstobj, maxlen) + '] \xd7 ' + obj.length;
      }
      return '[' + pieces.join(', ') + ']';
    } else if (isshort(obj, false, maxlen)) {
      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          pieces.push(quotekey(key) + ': ' + tiny(obj[key], maxlen));
        }
      }
      return (vt == 'Object' ? '{' : vt + '{') + pieces.join(', ') + '}';
    }
    if (vt == 'Array') { return 'Array(' + obj.length + ')'; }
    return vt;
  }
  function quotekey(k) {
    if (/^\w+$/.exec(k)) { return k; }
    return cstring(k);
  }
  function htmlescape(s, q) {
    var pat = /[<>&]/g;
    if (q) { pat = new RegExp('[<>&' + q + ']', 'g'); }
    return s.replace(pat, function(c) {
      return c == '<' ? '&lt;' : c == '>' ? '&gt;' : c == '&' ? '&amp;' :
             c == '"' ? '&quot;' : '&#' + c.charCodeAt(0) + ';';
    });
  }
  function unindented(s) {
    s = s.replace(/^\s*\n/, '');
    var leading = s.match(/^\s*\S/mg);
    var spaces = leading.length && leading[0].length - 1;
    var j = 1;
    // If the block begins with a {, ignore those spaces.
    if (leading.length > 1 && leading[0].trim() == '{') {
      spaces = leading[1].length - 1;
      j = 2;
    }
    for (; j < leading.length; ++j) {
      spaces = Math.min(leading[j].length - 1, spaces);
      if (spaces <= 0) { return s; }
    }
    var removal = new RegExp('^\\s{' + spaces + '}', 'mg');
    return s.replace(removal, '');
  }
  function expand(prefix, obj, depth, output) {
    output.push('<label class="_log"><input type="checkbox"><span>');
    if (prefix) { output.push(prefix); }
    if (isdom(obj)) {
      var ds = domsummary(obj, 10);
      output.push(ds[0]);
      output.push('</span><ul>');
      for (var node = obj.firstChild; node; node = node.nextSibling) {
        if (isnonspace(node)) {
          if (node.nodeType == 3) {
            output.push('<li><samp>');
            output.push(unindented(node.textContent));
            output.push('</samp></li>');
          } else if (isshort(node, true, 20) || depth <= 1) {
            output.push('<li>' + summary(node, 20) + '</li>');
          } else {
            expand('', node, depth - 1, output);
          }
        }
      }
      output.push('</ul>');
      if (ds[1]) {
        output.push('<span>');
        output.push(ds[1]);
        output.push('</span>');
      }
      output.push('</label>');
    } else {
      output.push(summary(obj, 10));
      output.push('</span><ul>');
      var vt = vtype(obj);
      if (vt == 'Function') {
        var ft = obj.toString();
        var m = /\{(?:.|\n)*$/.exec(ft);
        if (m) { ft = m[0]; }
        output.push('<li><samp>');
        output.push(htmlescape(unindented(ft)));
        output.push('</samp></li>');
      } else if (vt == 'Error') {
        output.push('<li><samp>');
        output.push(htmlescape(obj.stack));
        output.push('</samp></li>');
      } else if (vt == 'Array') {
        for (var j = 0; j < Math.min(100, obj.length); ++j) {
          try {
            val = obj[j];
          } catch(e) {
            val = e;
          }
          if (isshort(val, true, 20) || depth <= 1 || vtype(val) == 'global') {
            output.push('<li>' + j + ': ' + summary(val, 100) + '</li>');
          } else {
            expand(j + ': ', val, depth - 1, output);
          }
        }
        if (obj.length > 100) {
          output.push('<li>length=' + obj.length + ' ...</li>');
        }
      } else {
        var count = 0;
        for (var key in obj) {
          if (obj.hasOwnProperty(key)) {
            count += 1;
            if (count > 100) { continue; }
            var val;
            try {
              val = obj[key];
            } catch(e) {
              val = e;
            }
            if (isshort(val, true, 20) || depth <= 1 || vtype(val) == 'global') {
              output.push('<li>');
              output.push(quotekey(key));
              output.push(': ');
              output.push(summary(val, 100));
              output.push('</li>');
            } else {
              expand(quotekey(key) + ': ', val, depth - 1, output);
            }
          }
        }
        if (count > 100) {
          output.push('<li>' + count + ' properties total...</li>');
        }
      }
      output.push('</ul></label>');
    }
  }
  function initlogcss() {
    if (!addedcss && !window.document.getElementById('_logcss')) {
      var style = window.document.createElement('style');
      style.id = '_logcss';
      style.innerHTML = (linestyle ? 'samp._log{' +
          linestyle + '}' : '') + logcss;
      window.document.head.appendChild(style);
      addedcss = true;
    }
  }
  function repr(obj, depth, aoutput) {
    depth = depth || 3;
    var output = aoutput || [];
    var vt = vtype(obj);
    if (vt == 'Error' || vt == 'ErrorEvent') {
      output.push('<span style="color:red;">');
      expand('', obj, depth, output);
      output.push('</span>');
    } else if (isprimitive(vt)) {
      output.push(tiny(obj));
    } else if (isshort(obj, true, 100) || depth <= 0) {
      output.push(summary(obj, 100));
    } else {
      expand('', obj, depth, output);
    }
    if (!aoutput) {
      return output.join('');
    }
  }
  function aselement(s, def) {
    switch (typeof s) {
      case 'string':
        if (s == 'body') { return document.body; }
        if (document.querySelector) { return document.querySelector(s); }
        if ($) { return $(s)[0]; }
        return null;
      case 'undefined':
        return def;
      case 'boolean':
        if (s) { return def; }
        return null;
      default:
        return s;
    }
    return null;
  }
  function stickscroll() {
    var stick = false, a = aselement(autoscroll, null);
    if (a) {
      stick = a.scrollHeight - a.scrollTop - 10 <= a.clientHeight;
    }
    if (stick) {
      return (function() {
        a.scrollTop = a.scrollHeight - a.clientHeight;
      });
    } else {
      return (function() {});
    }
  }
  function flushqueue() {
    var elt = aselement(logelement, null);
    if (elt && elt.appendChild && queue.length) {
      initlogcss();
      var temp = window.document.createElement('samp');
      temp.innerHTML = queue.join('');
      queue.length = 0;
      var complete = stickscroll();
      while ((child = temp.firstChild)) {
        elt.appendChild(child);
      }
      complete();
    }
    if (!retrying && queue.length) {
      retrying = setTimeout(function() { timer = null; flushqueue(); }, 100);
    } else if (retrying && !queue.length) {
      clearTimeout(retrying);
      retrying = null;
    }
  }
  
  // ---------------------------------------------------------------------
  // TEST PANEL SUPPORT
  // ---------------------------------------------------------------------
  var addedpanel = false;
  var inittesttimer = null;
  var abbreviate = [{}.undefined];
  
  function show(flag) {
    if (!addedpanel) { return; }
    if (arguments.length === 0 || flag) {
      $('#_testpanel').show();
    } else {
      $('#_testpanel').hide();
    }
  }
  function clear() {
    if (!addedpanel) { return; }
    $('#_testlog').find('._log').not('#_testpaneltitle').remove();
  }
  function promptcaret(color) {
    return '<samp style="position:absolute;left:0;font-size:120%;color:' + color +
        ';">&gt;</samp>';
  }
  function getSelectedText(){
      if(window.getSelection) { return window.getSelection().toString(); }
      else if(document.getSelection) { return document.getSelection(); }
      else if(document.selection) {
          return document.selection.createRange().text; }
  }
  function formattitle(title) {
    return '<samp class="_log" id="_testpaneltitle" style="font-weight:bold;">' +
        title + '</samp>';
  }
  function readlocalstorage() {
    if (!uselocalstorage) {
      return;
    }
    var state = { height: panelheight, history: [] };
    try {
      var result = window.JSON.parse(window.localStorage[uselocalstorage]);
      if (result && result.slice && result.length) {
        // if result is an array, then it's just the history.
        state.history = result;
        return state;
      }
      $.extend(state, result);
    } catch(e) {
    }
    return state;
  }
  function updatelocalstorage(state) {
    if (!uselocalstorage) {
      return;
    }
    var stored = readlocalstorage(), changed = false;
    if ('history' in state &&
        state.history.length &&
        (!stored.history.length ||
        stored.history[stored.history.length - 1] !==
        state.history[state.history.length - 1])) {
      stored.history.push(state.history[state.history.length - 1]);
      changed = true;
    }
    if ('height' in state && state.height !== stored.height) {
      stored.height = state.height;
      changed = true;
    }
    if (changed) {
      window.localStorage[uselocalstorage] = window.JSON.stringify(stored);
    }
  }
  function wheight() {
    return window.innerHeight || $(window).height();
  }
  function tryinitpanel() {
    if (addedpanel) {
      if (paneltitle) {
        if ($('#_testpaneltitle').length) {
          $('#_testpaneltitle').html(paneltitle);
        } else {
          $('#_testlog').prepend(formattitle(paneltitle));
        }
      }
    } else {
      if (!window.document.getElementById('_testlog') && window.document.body) {
        initlogcss();
        var state = readlocalstorage();
        var titlehtml = (paneltitle ? formattitle(paneltitle) : '');
        if (state.height > wheight() - 50) {
          state.height = Math.min(wheight(), Math.max(10, wheight() - 50));
        }
        $('body').prepend(
          '<samp id="_testpanel" style="overflow:hidden;' +
              'position:fixed;bottom:0;left:0;width:100%;height:' + state.height +
              'px;background:rgba(240,240,240,0.8);' +
              'font:10pt monospace;z-index:9999;' +
              // This last bit works around this position:fixed bug in webkit:
              // https://code.google.com/p/chromium/issues/detail?id=128375
              '-webkit-transform:translateZ(0);">' +
            '<samp id="_testdrag" style="' +
                'cursor:row-resize;height:6px;width:100%;' +
                'display:block;background:lightgray"></samp>' +
            '<samp id="_testscroll" style="overflow-y:scroll;overflow-x:hidden;' +
               'display:block;width:100%;height:' + (state.height - 6) + 'px;">' +
              '<samp id="_testlog" style="display:block">' +
              titlehtml + '</samp>' +
              '<samp style="position:relative;display:block;">' +
              promptcaret('blue') +
              '<input id="_testinput" class="_log" style="width:100%;' +
                  'padding-left:1em;margin:0;border:0;font:inherit;' +
                  'background:rgba(255,255,255,0.8);">' +
             '</samp>' +
          '</samp>');
        addedpanel = true;
        flushqueue();
        var historyindex = 0;
        var historyedited = {};
        $('#_testinput').on('keydown', function(e) {
          if (e.which == 13) {
            // Handle the Enter key.
            var text = $(this).val();
            $(this).val('');
            // Save (nonempty, nonrepeated) commands to history and localStorage.
            if (text.trim().length &&
                (!state.history.length ||
                 state.history[state.history.length - 1] !== text)) {
              state.history.push(text);
              updatelocalstorage({ history: [text] });
            }
            // Reset up/down history browse state.
            historyedited = {};
            historyindex = 0;
            // Copy the entered prompt into the log, with a grayed caret.
            loghtml('<samp class="_log" style="margin-left:-1em;">' +
                    promptcaret('lightgray') +
                    htmlescape(text) + '</samp>');
            $('#_testscroll').scrollLeft(0);
            $(this).select();
            // Deal with the ":scope" command
            if (text.trim().length && text.trim()[0] == ':') {
              var scopename = text.trim().substring(1).trim();
              if (!scopename || scopes.hasOwnProperty(scopename)) {
                currentscope = scopename;
                var desc = scopename ? 'scope ' + scopename : 'default scope';
                loghtml('<span style="color:blue">switched to ' + desc + '</span>');
              } else {
                loghtml('<span style="color:red">no scope ' + scopename + '</span>');
              }
              return;
            }
            // Actually execute the command and log the results (or error).
            try {
              var result = seeeval(currentscope, text);
              for (var j = abbreviate.length - 1; j >= 0; --j) {
                if (result === abbreviate[j]) break;
              }
              if (j < 0) {
                loghtml(repr(result));
              }
            } catch (e) {
              see(e);
            }
          } else if (e.which == 38 || e.which == 40) {
            // Handle the up and down arrow keys.
            // Stow away edits in progress (without saving to history).
            historyedited[historyindex] = $(this).val();
            // Advance the history index up or down, pegged at the boundaries.
            historyindex += (e.which == 38 ? 1 : -1);
            historyindex = Math.max(0, Math.min(state.history.length,
                historyindex));
            // Show the remembered command at that slot.
            var newval = historyedited[historyindex] ||
                state.history[state.history.length - historyindex];
            if (typeof newval == 'undefined') { newval = ''; }
            $(this).val(newval);
            this.selectionStart = this.selectionEnd = newval.length;
            e.preventDefault();
          }
        });
        $('#_testdrag').on('mousedown', function(e) {
          var drag = this,
              dragsum = $('#_testpanel').height() + e.pageY,
              barheight = $('#_testdrag').height(),
              dragwhich = e.which,
              dragfunc;
          if (drag.setCapture) { drag.setCapture(true); }
          dragfunc = function dragresize(e) {
            if (e.type != 'blur' && e.which == dragwhich) {
              var winheight = wheight();
              var newheight = Math.max(barheight, Math.min(winheight,
                  dragsum - e.pageY));
              var complete = stickscroll();
              $('#_testpanel').height(newheight);
              $('#_testscroll').height(newheight - barheight);
              complete();
            }
            if (e.type == 'mouseup' || e.type == 'blur' ||
                e.type == 'mousemove' && e.which != dragwhich) {
              $(window).off('mousemove mouseup blur', dragfunc);
              if (document.releaseCapture) { document.releaseCapture(); }
              if ($('#_testpanel').height() != state.height) {
                state.height = $('#_testpanel').height();
                updatelocalstorage({ height: state.height });
              }
            }
          };
          $(window).on('mousemove mouseup blur', dragfunc);
          return false;
        });
        $('#_testpanel').on('mouseup', function(e) {
          if (getSelectedText()) { return; }
          // Focus without scrolling.
          var scrollpos = $('#_testscroll').scrollTop();
          $('#_testinput').focus();
          $('#_testscroll').scrollTop(scrollpos);
        });
      }
    }
    if (inittesttimer && addedpanel) {
      clearTimeout(inittesttimer);
    } else if (!addedpanel && !inittesttimer) {
      inittesttimer = setTimeout(tryinitpanel, 100);
    }
  }
  
  exportsee();
  
  })();
  