1d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelvar notify = [];
2d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelvar experimentIdCounter = 0;
3d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel/**
4d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * The questions above are answered by running a bunch of experiments
5d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * exhaustively for all combinations of HTML element names.
6d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *
7d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * @param makeHtmlString takes one or more element names.
8d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *    Its {@code length} property specifies its arity, and runExperiment
9d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *    calls it iteratively with every permutation of length element names.
10d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * @param checkDom receives the element names passed to makeHtmlString,
11d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *    an HTML document body created by parsing the HTML from makeHtmlString
12d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *    and initialResult/return value from last call to checkDom.
13d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * @param initialResult the first result value to pass to checkDom.
14d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel * @param opt_elementNames an array of element names which defaults to
15d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel *    window.elementNames.
16d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel */
17d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction runExperiment(makeHtmlString, checkDom, initialResult, onResult,
18d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel                       opt_elementNames) {
19d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var experimentIndex = ++experimentIdCounter;
20d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var iframes = document.getElementById('experiment-iframes');
21d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var iframe = document.createElement('iframe');
22d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  iframes.appendChild(iframe);
23d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
24d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var elementNames = opt_elementNames || window.elementNames;
25d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
26d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var nElements = elementNames.length;
27d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var arity = makeHtmlString.length;
28d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var nRuns = Math.pow(nElements, arity);
29d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var runIndex = 0;
30d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var paramIndices = new Array(arity);
31d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var paramValues  = new Array(arity);
32d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var i = 0; i < arity; ++i) {
33d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    paramIndices[i] = 0;
34d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    paramValues[i] = elementNames[0];
35d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
36d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var exhausted = nRuns === 0;
37d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
38d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var progressCounterContainer =
39d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    document.getElementById('experiment-progress-counter');
40d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
41d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var startTime = Date.now();
42d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var lastProgressUpdateTime = startTime;
43d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
44d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var result = initialResult;
45d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
46d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var progressCounter;
47d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  if (progressCounterContainer) {
48d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    progressCounter = document.createElement('li');
49d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    progressCounter.style.width = '0';
50d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    progressCounterContainer.appendChild(progressCounter);
51d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
52d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
53d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  function advance() {
54d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    // Advance to next permutation.
55d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var i;
56d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    for (i = arity; --i >= 0;) {
57d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (++paramIndices[i] < nElements) {
58d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        paramValues[i] = elementNames[paramIndices[i]];
59d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        break;
60d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
61d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      paramIndices[i] = 0;
62d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      paramValues [i] = elementNames[0];
63d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
64d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    ++runIndex;
65d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (progressCounter) {
66d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var now = Date.now();
67d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (now - lastProgressUpdateTime > 250 ) {
68d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var ratio = runIndex / nRuns;
69d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        progressCounter.style.width = (100 * ratio).toFixed(2) + '%';
70d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        lastProgressUpdateTime = now;
71d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var timeSoFar = now - startTime;
72d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (timeSoFar > 5000) {
73d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          // Assuming time per run is constant:
74d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          // total_time / nRuns = time_so_far / runIndex
75d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          // total_time = time_so_far * nRuns / runIndex
76d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          //            = time_so_far / ratio
77d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          // eta = total_time - time_so_far
78d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          //     = time_so_far / ratio - time_so_far
79d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          //     = time_so_far * (1/ratio - 1)
80d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          var eta = timeSoFar * (1 / ratio - 1);
81d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          progressCounter.innerHTML = eta > 250
82d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel              ? 'ETA:' + (eta / 1000).toFixed(1) + 's' : '';
83d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
84d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
85d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
86d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    exhausted = i < 0;
87d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
88d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
89d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  function step() {
90d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var htmlString = null;
91d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    // Try to generate an HTML string.
92d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    // The maker can return a nullish value to abort or punt on an experiment,
93d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    // so we loop until we find work to do.
94d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    while (!exhausted) {
95d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      paramValues.length = arity;
96d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      htmlString = makeHtmlString.apply(null, paramValues);
97d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (htmlString != null) {
98d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        break;
99d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
100d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      advance();
101d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
102d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
103d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (htmlString == null) {
104d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var endTime = Date.now();
105d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      console.log('experiment took %d millis for %d runs',
106d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel                  (endTime - startTime), nRuns);
107d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (progressCounter) {
108d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        setTimeout(function () {
109d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          iframes.removeChild(iframe);
110d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          progressCounterContainer.removeChild(progressCounter);
111d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }, 250);
112d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
113d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      onResult(result);
114d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    } else {
115d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var notifyIndex = notify.indexOf(void 0);
116d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (notifyIndex < 0) { notifyIndex = notify.length; }
117d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      notify[notifyIndex] = function () {
118d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        notify[notifyIndex] = void 0;
119d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
120d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        // Process result
121d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        paramValues[arity] = iframe.contentDocument.body;
122d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        paramValues[arity + 1] = result;
123d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        result = checkDom.apply(null, paramValues);
124d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        paramValues.length = arity;
125d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
126d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        // Requeue the next step on the parent frames event queue.
127d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        setTimeout(function () { advance(); step(); }, 0);
128d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      };
129d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      // Start the iframe parsing its body.
130d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      iframe.srcdoc = (
131d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        '<!doctype html><html><head></head>'
132d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        + '<body onload="parent.notify[' + notifyIndex + ']()">'
133d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        + htmlString
134d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      );
135d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
136d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
137d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  step();
138d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
139d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
140d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction formatDataToJsonHTML(data) {
141d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var out = [];
142d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var htmlForNullValue = '<span class="json-kw">null</span>';
143d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var htmlForErrorValue = '<span class="json-kw json-err">null</span>';
144d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var depth = 0;
145d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var spaces = '                ';
146d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  format(data);
147d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return out.join('');
148d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
149d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  function format(v) {
150d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (v == null) {
151d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      out.push(htmlForNullValue);
152d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      return;
153d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
154d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var t = typeof v;
155d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (t === 'boolean') {
156d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      out.push('<span class="json-kw">', v, '</span>');
157d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    } else if (t === 'number') {
158d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (isFinite(v)) {
159d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        out.push('<span class="json-val">', v, '</span>');
160d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      } else {
161d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        out.push(htmlForErrorValue);
162d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
163d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    } else if (t === 'string' || v instanceof String) {
164d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var token = JSON.stringify(String(v));
165d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      token = token.replace(/&/g, '&amp;').replace(/</g, '&lt;');
166d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      out.push('<span class="json-str">', token, '</span>');
167d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    } else {
168d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var length = v.length;
169d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var isSeries = ('number' === typeof length
170d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel                      && length === (length & 0x7fffffff));
171d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      // Don't put properties on their own line if there are only a few.
172d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var inlinePropLimit = isSeries ? 8 : 4;
173d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var inline = true;
174d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var numProps = 0;
175d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      for (var k in v) {
176d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (!Object.hasOwnProperty.call(v, k)) { continue; }
177d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var propValue = v[k];
178d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if ((propValue != null && typeof propValue == 'object')
179d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel            || ++numProps > inlinePropLimit) {
180d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          inline = false;
181d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          break;
182d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
183d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
184d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      // Put the appropriate white-space inside brackets and after commas.
185d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      function maybeIndent(afterComma) {
186d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (inline) {
187d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          if (afterComma) { out.push(' '); }
188d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        } else {
189d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          out.push('\n');
190d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          var nSpaces = depth * 2;
191d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          while (nSpaces > 0) {
192d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel            var nToPush = Math.min(nSpaces, spaces.length);
193d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel            out.push(spaces.substring(0, nToPush));
194d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel            nSpaces -= nToPush;
195d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          }
196d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
197d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
198d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var onclick = depth
199d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        ? ' onclick="return toggleJsonBlock(this, event)"'
200d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        : '';
201d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      // Mark blocks so that we can do expandos on collections.
202d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      out.push('<span class="json-ext json-block-', depth,
203d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               depth === 0 || inline ? ' json-nocollapse' : '',
204d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               '"', onclick, '>',
205d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               isSeries ? '[' : '{',
206d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               // Emit link-like ellipses that can serve as a button for
207d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               // expando-ness.
208d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               '<span class="json-ell">&hellip;</span>',
209d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel               '<span class="json-int">');
210d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      ++depth;
211d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (isSeries) {
212d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        for (var i = 0; i < length; ++i) {
213d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          if (i) { out.push(','); }
214d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          maybeIndent(i !== 0);
215d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          format(v[i]);
216d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
217d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      } else {
218d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var needsComma = false;
219d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        for (var k in v) {
220d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          if (!Object.hasOwnProperty.call(v, k)) { continue; }
221d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          if (needsComma) {
222d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel            out.push(',');
223d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          }
224d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          maybeIndent(needsComma);
225d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          out.push('<span class="json-prop">');
226d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          format(String(k));
227d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          out.push(': ');
228d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          format(v[k]);
229d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          out.push('</span>');
230d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          needsComma = true;
231d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
232d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
233d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      --depth;
234d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      maybeIndent(false);
235d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      out.push('</span>', isSeries ? ']' : '}', '</span>');
236d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
237d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
238d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
239d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
240d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction displayJson(data, container) {
241d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  container.innerHTML = formatDataToJsonHTML(data);
242d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
243d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
244d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction toggleJsonBlock(el, event) {
245d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  event && event.stopPropagation && event.stopPropagation();
246d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var className = el.className;
247d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var classNameCollapsed = className.replace(/\bjson-expanded\b/g, '');
248d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  className = className === classNameCollapsed
249d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      ? className + ' json-expanded' : classNameCollapsed;
250d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  className = className.replace(/^ +| +$| +( [^ ])/g, "$1");
251d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  el.className = className;
252d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return false;
253d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
254d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
255d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction Promise() {
256d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  if (!(this instanceof Promise)) { return new Promise(); }
257d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  this.paused = [];
258d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  this.satisfy = function () {
259d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var paused = this.paused;
260d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelconsole.log('satisfying ' + paused.length);
261d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    for (var i = 0, n = paused.length; i < n; ++i) {
262d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      setTimeout(paused[i], 0);
263d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
264d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    this.paused.length = 0;
265d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  };
266d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
267d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelPromise.prototype.toString = function () { return "Promise"; };
268d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction when(f, var_args) {
269d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var unsatisfied = [];
270d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var i = 1, n = arguments.length; i < n; ++i) {
271d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var argument = arguments[i];
272d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (argument instanceof Promise) {
273d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      unsatisfied.push(argument);
274d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
275d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
276d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var nToWaitFor = unsatisfied.length;
277d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  if (nToWaitFor) {
278d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var pauser = function pauser() {
279d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (!--nToWaitFor) {
280d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        setTimeout(f, 0);
281d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
282d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    };
283d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    for (var j = 0; j < nToWaitFor; ++j) {
284d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      unsatisfied[j].paused.push(pauser);
285d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
286d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    unsatisfied = null;
287d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  } else {
288d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    setTimeout(f, 0);
289d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
290d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
291d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
292d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction newBlankObject() {
293d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return (Object.create || Object)(null);
294d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
295d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
296d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction getOwn(o, k, opt_default) {
297d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return Object.hasOwnProperty.call(o, k) ? o[k] : opt_default;
298d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
299d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
300d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction breadthFirstSearch(start, isEnd, eq, adjacent) {
301d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var stack = [{ node: start, next: null }];
302d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  while (stack.length) {
303d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var candidate = stack.shift();
304d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (isEnd(candidate.node)) {
305d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var path = [candidate.node];
306d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      while (candidate.next) {
307d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        candidate = candidate.next;
308d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        path.push(candidate.node);
309d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
310d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      return path;
311d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
312d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var adjacentNodes = adjacent(candidate.node);
313d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    adj:
314d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    for (var i = 0, n = adjacentNodes.length; i < n; ++i) {
315d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var adjacentNode = adjacentNodes[i];
316d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      for (var dupe = candidate; dupe; dupe = dupe.next) {
317d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (eq(dupe.node, adjacentNode)) { continue adj; }
318d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
319d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      stack.push({ node: adjacentNode, next: candidate });
320d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
321d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
322d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return null;
323d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
324d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
325d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction reverseMultiMap(multimap) {
326d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var reverse = newBlankObject();
327d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var k in multimap) {
328d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (Object.hasOwnProperty.call(multimap, k)) {
329d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      var values = multimap[k];
330d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      for (var i = 0, n = values.length; i < n; ++i) {
331d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var value = values[i];
332d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        var reverseKeys = getOwn(reverse, value) || [];
333d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        reverse[value] = reverseKeys;
334d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        reverseKeys.push(k);
335d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      }
336d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
337d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
338d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return reverse;
339d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
340d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
341d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction innerTextOf(element) {
342d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  function appendTextOf(node, out) {
343d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    switch (node.nodeType) {
344d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      case 1:  // Element
345d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        for (var c = node.firstChild; c; c = c.nextSibling) {
346d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel          appendTextOf(c, out);
347d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        }
348d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        break;
349d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      case 3: case 4: case 6:  // Text / CDATA / Entity
350d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        out.push(node.nodeValue);
351d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        break;
352d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
353d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
354d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var buf = [];
355d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  if (element) { appendTextOf(element, buf); }
356d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return buf.join('');
357d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
358d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
359d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction sortedMultiMap(mm) {
360d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var props = [];
361d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var k in mm) {
362d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (!Object.hasOwnProperty.call(mm, k)) { continue; }
363d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var v = mm[k];
364d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    if (v instanceof Array) {
365d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      v = v.slice();
366d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      v.sort();
367d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
368d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    props.push([k, v]);
369d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
370d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  props.sort(
371d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      function (a, b) {
372d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        a = a[0];
373d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        b = b[0];
374d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (a < b) { return -1; }
375d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        if (b < a) { return 1; }
376d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel        return 0;
377d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      });
378d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var sorted = newBlankObject();
379d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var i = 0, n = props.length; i < n; ++i) {
380d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    var prop = props[i];
381d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    sorted[prop[0]] = prop[1];
382d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
383d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return sorted;
384d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
385d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
386d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction makeSet(strs) {
387d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  var s = newBlankObject();
388d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  for (var i = 0, n = strs.length; i < n; ++i) {
389d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    s[strs[i]] = s;
390d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
391d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return s;
392d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
393d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
394d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction inSet(s, str) {
395d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return s[str] === s;
396d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
397d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
398d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction elementContainsComment(el) {
399d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return elementContainsNodeOfType(el, 8);
400d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
401d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
402d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction elementContainsText(el) {
403d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  return elementContainsNodeOfType(el, 3);
404d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
405d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel
406d619c4a1a90430a4111eb71444350aa321a289dbmikesamuelfunction elementContainsNodeOfType(el, nodeType) {
407d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  if (el) {
408d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    for (var c = el.firstChild; c; c = c.nextSibling) {
409d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel      if (c.nodeType === nodeType) { return true; }
410d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    }
411d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel    return false;
412d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel  }
413d619c4a1a90430a4111eb71444350aa321a289dbmikesamuel}
414