PageSourceSearch

https://puma.io/puma5/js/y_app.js

js puma.io collected 2026-09-24 08:42:39 UTC 127,330 bytes, 4,306 lines download raw bytes

1; 'use strict';
2
3var t2GA;
4
5if ( /github\.io/i.test(location.hostname) ) {
6  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
7  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
8  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
9  })(window,document,'script','https://www.google-analytics.com/analytics.js','t2GA');
10}
11
12/*
13if ('ontouchstart' in document.documentElement) {
14  window.onerror = function (msg, url, lineNo, columnNo, error) {
15      var string = msg.toLowerCase(),
16          substring = "script error";
17
18      if (/script error/i.test(string)) {
19          alert('Script Error: See Browser Console for Detail');
20      } else {
21          var message =
22              "Message: " + msg    +   "\nURL: "  + url +
23              "\nLine: "  + lineNo + "\nColumn: " + columnNo +
24              "\nError object: "   + JSON.stringify(error);
25          alert(message);
26      }
27      return false;
28  };
29};
30*/
31
32/* This is an immediate function that wraps everything.  It initializes all of
33 * the app constants and variables, and hooks window.onDOMContentLoaded to
34 * {eDOMContent}. Inside of it the Panes module is defined, also an immediate
35 * function.
36 *
37 * Many functions accept a content parameter.  On first load, this is the main
38 * document div#content element.  After that, the parameter is the div#content
39 * element in the documentFragment from the XHR request.  This is done so that
40 * all document modifications / additions are done **before** the element
41 * replaces the document element.  Simply put, faster rendering.
42 * @js_module_new App
43 */
44(function(_t2Info, undefined) {
45
46var t2GAInit = false;
47
48if (t2GA instanceof Function) {
49  t2GA( 'create', 'UA-79746991-1', 'auto');
50  t2GA( 'set', 'anonymizeIp', true);
51  t2GAInit = true;
52};
53
54//{ Constants
55
56/*### Constants */
57var ST_VERS = '0.6.0',  // state version (local storage object)
58    VERSION = '0.7.0';
59
60/*#### Window Metrics */
61var SML_MED = 110  ,    // char width 0 to 1 type
62    MED_LRG = 160  ,    // char width 1 to 2 type
63    ZOOM    =  95.0,    // char width for touch device zoom
64    FS      =  15.0;    // default fontSize px30em
65
66/*#### Size Metrics */
67var  FLT_TOP =  4.4,      // Top of Floating Pane    em
68     FLT_HGT =  4.8;      // Top + bottom (0.4 em)   em
69     WAIT_NO_TOUCH = 6.0  // Wait indicator          em
70     WAIT_TOUCH    = 9.0  // Wait indicator          em
71
72/*#### CSS Class Names */
73var CN_CLICKED = 'clicked', // used for 'clicked' highlight
74    CN_HIDDEN  = 'h',       // used with summaries, lists, & header buttons
75    CN_VISIBLE = 'o',
76    CN_WAIT    = 'w',       // shows wait cursor
77    CN_BTN_VIS  = 'vis';    // Used with button elements
78
79/*#### Text Strings */
80var T_VIEW_SRC = 'view source',
81    T_HIDE_SRC = 'hide source';
82
83/*#### clickedBy enumeration */
84var CB_CONTENT   = 0,
85    CB_OBJ_PATH  = 1,
86    CB_LIST      = 2,
87    CB_TOC       = 3,
88    CB_POP_STATE = 4;
89
90/*#### Valid List search characters */
91var RE_LIST_SEARCH = /[a-z0-9$!%&\*\+\-\/<=>\?\[\]\^_\|\~]/i;
92
93/*#### Pane Button ids */
94var HDR_PANE_BTNS  = ['list_vis', 'toc_vis'];
95
96/*#### Pane KeyDown Test */
97var WIN_PANE_KD    = 'cdlmpst'; // C-lass D-oc L-ist M-ethod P-roperties S-earch T-OC
98
99//}
100
101/*  t2Info.CSEP   t2Info.ISEP   */
102
103//{ Variables
104
105/*### Variables */
106var storage,  // set to localStorage, then sessionStorage
107    state;    // Display state object
108
109/*#### General Window Metrics & Data */
110var cS = null,
111    eContent,               // main content element
112    winHeight = 0,          // current window height
113    winWidth = 0,           // current window width
114    fwHdrHgt = 0,
115    winResizeTmrId = null,  // SetTimeout id for window.onresize
116    winType = -1,           // 0 narrow, 1 medium, 2 wide
117    winTitle = '',      // Title from <head><title>, used in history
118    e25vh,              // 25vh wide the element
119    px30em   = 0.0,     // 30em wide px
120    px50Char = 0.0,     // 50 monospaced characters wide px
121    waitWidth;          // width of wait indicator
122
123/*#### Clicked and Navigation */
124var aDOMCur = document.createElement('a'),  // current window.location
125    aDOMNext = document.createElement('a'), // next window.location
126    clickedBy = -1,  // source of navigation click
127    eContentClicked, // Element that is highlighted in main document
128    clickedTop = 0,  // screenY coordinate of click (or clicked item)
129    oXHRDoc,         // xhr object currently retrieving document
130    oCSSRuleTable;
131
132/*#### App State */
133var isHttp = /^http(s)?:/.test(window.location.protocol),
134    isFirstLoad = true,
135    isGetElByIdInDocFrag = ('getElementById' in document.createDocumentFragment()),
136    isServer = false,
137    isTouch = ('ontouchstart' in document.documentElement),
138    isVHBad = false,        // iOS Safari 100vh != innerHeight
139    isWinHistory = ('history' in window) && ('pushState' in window.history) &&
140      (window.history.pushState instanceof Function),
141    isWinKPTimeOut = false,  // used to debounce long key presses
142    isZoomed = false;        // Touch devices will zoom via FontSize
143
144/*#### Lib Info */
145var elLibFooter;
146
147/*#### Debug flags & Objects */
148var dbgDocTiming   = false, // logs doc load timing info to console, dbgDocLoad
149    dbgDoc         = false, // logs doc info to console, dbgDocInfo
150    dbgListCalls   = false, // logs list function calls
151    dbgListTiming  = false, // logs list load timing info to console, dbgListLoad
152    dbgListSplit   = false,
153    dbgSearchLoad  = false,
154    dbgSearchSplit = false,
155    dbgYDebug      = false,  // use y_debug element
156    oDbgDoc   = {},
157    oDbgList  = {};
158
159/*#### Objects created by Pane module */
160var oPane, oList, oToc;
161
162var LS_XHR_LOAD = 0,
163    LS_RENDER = 1;
164
165/* t2Opts (t2 Options) holds data from YARD and customization data */
166  var docLoadState = { xhr: null, cbFunc: null, url: '', state: null },
167      t2Opts = { NSEP: '', customHeaderId: '' };
168
169//}
170
171/* This code controls the 'List' and 'TOC' panes.  Exported functions are
172 * detailed in the {__loadPublicState} function, which loads oPane, oList, & oToc.
173 * All exported functions have an underscore in their name.
174 *
175 * Three objects are exported:
176 * * oPane - functions that are common to (or affect) both oList & oToc
177 * * oList - functions & properties specific to the 'List' pane
178 * * oToc  - functions & properties specific to the 'TOC' pane
179 *
180 * The functions are grouped and named the same.
181 * @js_module_new Panes
182 */
183(function() {
184
185//{ Pane Constants
186
187/*### Constants
188 *#### Class Names */
189var CN_DOCKED   = 'd',         // Used with nav & toc div elements (the panes)
190    CN_FLOATING = 'f',
191    CN_TOUCH    = ' t',
192    CN_CLOSED   = 'yl_closed'; // Used on list li elements that have buttons
193
194/*#### Size Metrics - oList & oToc */
195var LIST_MIN = 18.0     , // List pane min width      em
196    LIST_MAX = 40.0     , // List pane max width      em
197    LIST_HDR =  5.866667, // List pane header height  em
198     TOC_MIN = 18.0     , // as above, but oToc
199     TOC_MAX = 30.0     ,
200     TOC_HDR =  4.00    ,
201     H_CLS   =  1.8667  ; // Height Class & oToc Item em
202
203/*#### .type enumeration */
204var LIST_CLASS     = 0,
205    LIST_METHOD    = 1,
206    LIST_EXCEPTION = 2,
207    LIST_PROPERTY  = 3,
208    LIST_FILE      = 4,
209    LIST_UNKNOWN   = 5,
210    // array must match enum, see listGetInfo
211    LIST_ENUM = ['class', 'method', 'exception', 'property', 'file', 'Unknown'];
212
213/*#### TOC Generate Strings & RegEx */
214var RE_METH_CLS  = /-class_method$/,
215    RE_METH_INST = /-instance_method$/;
216
217//}
218
219//{ Pane Variables
220
221/*### Variables
222 *#### Resizers start data */
223var resizerStWid = 0,  // starting pane width
224    resizerStX   = 0;  // mouseDown or TouchStart screenX
225
226/*#### Metrics */
227var winFloatingTop; // screenY of floating windows
228
229/*#### List Search & Load */
230var listLastSearchText = '',   // used for search cancel
231    listPath           = '',   // base path for list files
232    listCurPN          = null, // pathName of current list
233
234    oListData = {},       // data about lists, key is url
235    oLoad     = {},       // object to pass to listLoadSplit
236    oSearches = [];       // array of search objects
237
238/*#### TOC Search & Load */
239var tocLastSearchText = '',  // used for search cancel
240    tocSearchInfo = [],
241    re_csep = t2Info.CSEP.replace(/([\?*+^|\.\$\[\]\(\)\{\}])/g, "\\$1"),
242    re_isep = t2Info.ISEP.replace(/([\?*+^|\.\$\[\]\(\)\{\}])/g, "\\$1"),
243    re_meth_sep = new RegExp('^(' + re_csep + '|' + re_isep + ')');
244
245//}
246
247//{ @!group Public Interface
248
249/* Binds reSize event handlers to oList & oToc
250 * @param me [oList, oToc] this object
251 */
252function __bind(me) {
253  me.resizeMD = resizeMD.bind(me);
254  me.resizeMM = resizeMM.bind(me);
255  me.resizeMU = resizeMU.bind(me);
256
257  if (isTouch) {
258    me.resizeTS = resizeTS.bind(me);
259    me.resizeTM = resizeTM.bind(me);
260    me.resizeTE = resizeTE.bind(me);
261  }
262};
263
264/* Initialization - Loads public function properties into oList, oPane, and oToc,
265 * loads state from storage.
266 * @note This is loaded before the DOMContentLoaded event
267 */
268function __loadPublicState() {
269  var version = '';
270
271  try { storage = window.localStorage; } catch (e) {
272    try { storage = window.sessionStorage; } catch (e) { };
273  }
274
275  if (storage && storage.yardState) {
276    state = JSON.parse(storage.yardState);
277    if (state.showSource === undefined)
278      state.showSource = false;
279    version = (state.version !== undefined ? state.version : '');
280  }
281  if (ST_VERS > version) {
282    state = {
283               'version': ST_VERS,
284     'overviewCollapsed': false,
285    'relationsCollapsed': false,
286      'summaryCollapsed': false,
287            'showSource': false,
288              'fontSize': '15px',
289           'fontSizeSet': null,
290               'display': [
291        { 'list': { 'docked': false , 'vis': true  , 'width': 0 },
292          'toc' : { 'docked': false , 'vis': false , 'width': 0 } },
293
294        { 'widthDocked': 0,
295          'list': { 'docked': true  , 'vis': true  , 'width': 0 },
296          'toc' : { 'docked': true  , 'vis': false , 'width': 0 } },
297
298        { 'list': { 'docked': true  , 'vis': true  , 'width': 0 },
299          'toc' : { 'docked': true  , 'vis': true  , 'width': 0 } }
300      ]
301    };
302    Object.seal(state);
303    if (storage) storage.yardState = JSON.stringify(state);
304  }
305
306  oPane = {
307    firstDOMLoad: _FirstDOMLoad,     // called by eDOMContent (first load)
308         mainClk: pane_MainClk,      // called by clkHdrBtn
309          mainKD: pane_MainKD,       // called by winKeyDown
310          locSet: pane_LocSet        // called by domLayout
311  };
312
313  /* e element
314   * s style
315   * d state array item
316   */
317  oList = { e: null, s: null, d: null, bVis: null, nav: null,
318           clickedA: null, clickedLI: null, clickedLIOld: null, items: null,
319           isOpenClose: false,
320             hdrHeight: 0,
321            type: LIST_UNKNOWN,
322         itemQty: 0,
323     itemsSearch: 0,
324        itemsVis: 0,
325        keyPress: list_KeyPress,     // for single key list scroll
326            load: list_Load,         // called to navigate to new list
327     showClicked: list_ShowClicked   // called by gotoHash & xhrCBDoc
328  };
329
330  oToc = { e: null, s: null, d: null, bVis: null, nav: null,
331           clickedA: null, clickedLI: null, clickedLIOld: null, items: null,
332           isOpenClose: false,
333             hdrHeight: 0,
334          searchInfo: null,
335      floatingOffset: null,
336        generate: toc_Generate,      // called by addContent
337     showClicked: toc_ShowClicked    // called by gotoHash
338  };
339
340  oLoad.isRunning = false;
341  oLoad.cancel    = false;
342
343  __bind(oList);
344  __bind(oToc);
345  Object.seal(oPane);
346  Object.seal(oList);
347  Object.seal(oToc);
348  __bind = undefined;
349};
350
351//} @!endgroup
352
353//{ @!group FirstDOMLoad Functions
354
355/* Called **only** on first YARD document load
356 * @param [Boolean, nil] hookEvents true for events only
357 */
358function _FirstDOMLoad(hookEvents) {
359  if (hookEvents) {
360    _HookEvents();
361    _HookEvents  = undefined;
362  } else {
363    _CheckState();
364    _SetDOMProps();
365    _CheckState  = undefined;
366    _SetDOMProps = undefined;
367  };
368}
369
370/* Checks width's in state, intializes hdrHeights */
371function _CheckState() {
372  var em = px30em / 30.0,
373      st,
374      writeState = false;
375
376  winFloatingTop  = FLT_TOP  * em;
377  oList.hdrHeight = LIST_HDR * em;
378   oToc.hdrHeight = TOC_HDR  * em;
379
380  // seemed to be a good width...
381  em = (0.6667 * px50Char).toFixed(0);
382
383  // set up state with DOM measurement
384  for (var i = 0; i < 3; i++) {
385    st = state.display[i];
386    if (st.list.width === 0) {
387      st.list.width = em;
388      writeState = true;
389    }
390    if (st.toc.width === 0) {
391      st.toc.width = em;
392      writeState = true;
393    }
394  }
395  if (state.display[1].widthDocked === 0) {
396    st.widthDocked = em;
397    writeState = true;
398  }
399  if (writeState && storage)
400    storage.yardState = JSON.stringify(state);
401}
402
403/* Hooks oList & oToc events on first load */
404function _HookEvents() {
405  var el = _id('list_search_text');
406  el.tabIndex = 1;
407  el.addEventListener('keypress', listSearchKP , false);
408  el.addEventListener('input'   , listSearchChg, false);
409
410  el = _id('toc_search_text');
411  el.tabIndex = 2;
412  el.addEventListener('keypress', tocSearchKP , false);
413  el.addEventListener('input'   , tocSearchChg, false);
414
415  _id('list_header').addEventListener('click', listClkHeader, false);
416  _id('toc_header' ).addEventListener('click',  tocClkHeader, false);
417
418  oList.nav.addEventListener('click', paneClk.bind(oList), false);
419   oToc.nav.addEventListener('click', paneClk.bind(oToc) , false);
420
421  _id('list_sizer').addEventListener('mousedown',  oList.resizeMD, false);
422  _id('toc_sizer' ).addEventListener('mousedown',   oToc.resizeMD, false);
423
424  if (isTouch) {
425    _id('list_sizer').addEventListener('touchstart', oList.resizeTS, false);
426    _id('toc_sizer' ).addEventListener('touchstart',  oToc.resizeTS, false);
427  }
428}
429
430/* Loads properties of oList & oToc releated to elements on first load */
431function _SetDOMProps() {
432  oList.e     = _id('y_list');
433  oList.s     = _id('y_list').style;
434//  oList.bVis  = _id('list_vis');
435  oList.nav   = _id('list_nav');
436  oList.items = _id('list_items');
437
438  oToc.e     = _id('y_toc');
439  oToc.s     = _id('y_toc').style;
440//  oToc.bVis  = _id('toc_vis');
441  oToc.nav   = _id('toc_nav');
442  oToc.items = _id('toc_items');
443}
444
445//} @!endgroup
446
447//{ @!group oPane Functions
448
449/* Only called by domLayout, sets panes to state of state[winType]
450 * @param winTypeLast [Integer] previous winType (-1 on init)
451 */
452function pane_LocSet(winTypeLast) {
453  cS = state.display[winType];
454  oList.d = cS.list;
455  oToc.d  = cS.toc;
456  paneLocSet(oList, winTypeLast);
457  paneLocSet(oToc , winTypeLast);
458}
459
460/* Called when a button in the main header is clicked
461 * @param id [String] id of the clicked button
462 */
463function pane_MainClk(id) {
464  // IE11 var oMe = id.startsWith('list') ? oList : oToc;
465  var oMe = /^list/.test(id) ? oList : oToc;
466//  if ( id.endsWith('float') ) {
467  if ( /float$/.test(id) ) {
468    oMe.d.docked = false;
469    paneShow(oMe);
470  } else if ( /dock$/.test(id) ) {
471    oMe.d.docked = true;
472    paneShow(oMe);
473  } else if ( /vis$/.test(id) ) {
474    if (oMe.d.vis) paneHide(oMe);
475    else           paneShow(oMe);
476  }
477}
478
479/* Called when a key is pressed, from winKeyDown
480 * @param key [String] key pressed
481 */
482function pane_MainKD(key, which) {
483
484  // Escape key
485  if (which === 27) {
486    listSearchClear();
487    return;
488  }
489
490  if (WIN_PANE_KD.indexOf(key) < 0) return;
491  switch (key) {
492  case 'c':
493    listGoto('class');
494    break;
495  case 'd':
496    listGoto('doc');
497    break;
498  case 'l':
499    if (oList.d.vis) paneHide(oList);
500    else             paneShow(oList);
501    break;
502  case 'm':
503    listGoto('method');
504    break;
505  case 'p':
506    listGoto('properties');
507    break;
508  case 's':
509    if (!oList.d.vis) paneShow(oList);
510    _id('list_search_text').focus();
511    break;
512  case 't':
513    if (oToc.d.vis) paneHide(oToc);
514    else            paneShow(oToc);
515    break;
516  }
517}
518
519/* @!visibility private */
520
521/* Event handler for pane list click, bound to oList or oToc in paneHookEvents */
522function paneClk(e) {
523  var tgt = e.target,
524      eA,
525      eLI,
526      isList = (this === oList),
527      cancel = false,
528      t;
529
530  settingsCheck();  // close settings window
531  if (tgt.tagName === 'NAV' && tgt.classList.contains('y_nav')) return;
532
533  eLI = getPrntByIO(tgt, HTMLLIElement);
534
535  if (dbgListCalls) console.log("paneClk eLI\n" + eLI);
536
537  // clicked button of an open/close node, or an exception 'group' node
538  if ( (tgt instanceof HTMLButtonElement) ||
539    (tgt instanceof HTMLDivElement && !tgt.querySelector('a')) ) {
540
541    // close or open clicked node only
542    if (!e.ctrlKey) { eLI.classList.toggle(CN_CLOSED);
543
544    // close or open clicked node along with all children
545    } else {
546      var nl = eLI.querySelectorAll("li > div");
547      if ( eLI.classList.contains(CN_CLOSED) ) {
548        eLI.classList.remove(CN_CLOSED);
549        for (var i = 0, el; el = nl[i]; i++) {
550          el.parentElement.classList.remove(CN_CLOSED);
551        };
552      } else {
553        eLI.classList.add(CN_CLOSED);
554        for (var i = 0, el; el = nl[i]; i++) {
555          el.parentElement.classList.add(CN_CLOSED);
556        };
557      };
558    };
559    if (isList && this.isOpenClose) listEvenOdd();
560    cancel = true;
561
562  // clicked a document link
563  } else if ( eLI && ( eA = eLI.querySelector('a, div > a') ) && !e.ctrlKey) {
564    if (this.clickedLIOld) this.clickedLIOld.classList.remove(CN_CLICKED);
565    this.clickedLIOld = this.clickedLI;
566    clickedTop = eLI.getBoundingClientRect().top - fwHdrHgt;
567    if (!isList) clickedTop -= 1.0;   // bottom-border in TOC
568    this.clickedLI = eLI;
569    this.clickedA = eA;
570    clickedBy = ( isList ? CB_LIST : CB_TOC );
571
572    if (eA.hash !== '') {
573      // craziness for IE11 handling of anchor elements properties
574      t = eA.href.slice( eA.href.search(/[^:/]\//) + 1, eA.href.length - eA.hash.length);
575      if (t !== window.location.pathname) {
576        gotoDoc(eA.pathname + '#' + decodeURIComponent(eA.hash.replace(/%-/, '%25-')).slice(1));
577      } else {
578        aDOMNext.href = eA.href
579        gotoHash();
580      }
581    } else if ( !/\/#$/.test(eA.href) ) {
582      t = eA.getAttribute('href');
583      if ( /^\//.test(t) ) gotoDoc(t);
584      else                 gotoDoc("/" + t);
585    }
586    cancel = true;
587  };
588  if (cancel) {
589    e.preventDefault();
590    e.stopPropagation();
591    return false;
592  };
593}
594
595/* Hides a pane
596 * @param me [oList, oToc]
597 */
598function paneHide(me) {
599  me.bVis.className = CN_HIDDEN;
600  me.d.vis = false;
601  me.e.classList.add(CN_HIDDEN);
602  updateCSS();
603  if (storage) storage.yardState = JSON.stringify(state);
604}
605
606/* Sets location of panes, only called by pane_LocSet
607 * @param me [oList, oToc]
608 * @param winTypeLast [Integer] previous winType, -1 on first load
609 */
610function paneLocSet(me, winTypeLast) {
611  var isDocked = me.d.docked,
612      prevSt = winTypeLast >= 0 ? state.display[winTypeLast] : null,
613      cnLoc = isDocked ? CN_DOCKED : CN_FLOATING,
614      meLast = isFirstLoad || (me === oList ? prevSt.list : prevSt.toc),
615      dChng  = isFirstLoad || (me.d.docked !== meLast.docked);
616
617  if (dChng) {
618    cnLoc = isDocked ? CN_DOCKED : CN_FLOATING;
619    me.e.className = cnLoc + (isTouch ? CN_TOUCH : '');
620  }
621  if (me.d.vis) {
622    me.e.classList.remove(CN_HIDDEN);
623    if (me.bVis) me.bVis.className = CN_BTN_VIS;
624  } else {
625    me.e.classList.add(CN_HIDDEN);
626    if (me.bVis) me.bVis.className = CN_HIDDEN;
627  }
628  if (isDocked) {
629    me.s.width = ( winType === 1 ? cS.widthDocked : me.d.width ) + 'px';
630  } else {
631    me.s.width = '';
632  }
633
634}
635
636/* Makes a pane list item visible.  Only called by paneScrollTo.
637 * @param prnt [HTMLElement] item to make visible
638 * @return [Boolean] true any items were 'opened' to show the item
639 */
640function paneOpenToItem(prnt) {
641  var notFirst = false,
642      heightChanged = false,
643      open = [];
644
645  do {
646    if (prnt instanceof HTMLLIElement) {
647      if (notFirst) {
648        if ( prnt.classList.contains(CN_CLOSED) ) {
649          open.push(prnt);
650        }
651      } else notFirst = true;
652    }
653    prnt = prnt.parentElement;
654  }
655  while (prnt instanceof HTMLUListElement || prnt instanceof HTMLOListElement || prnt instanceof HTMLLIElement);
656
657  if (open.length > 0) {
658    heightChanged = true;
659    for (var i = 0, el; el = open[i]; i++) { el.classList.remove(CN_CLOSED); }
660  }
661  return heightChanged;
662}
663
664/* Called if a Pane global 'minus' (close) or 'plus' (open) button is clicked.
665 * @param me [oList, oToc] pane to change
666 * @param isOpen [Boolean]
667 */
668function paneOC(me, isOpen) {
669  var isList = (me === oList),
670      qsa = isList ? 'li > ul' : 'li > ol',
671      nl = me.items.querySelectorAll(qsa);
672
673  if (isOpen) {
674    for (var i = 0, el; el = nl[i]; i++) {
675      el.parentElement.classList.remove(CN_CLOSED);
676    }
677  } else {
678    for (var i = 0, el; el = nl[i]; i++) {
679      el.parentElement.classList.add(CN_CLOSED);
680    };
681  };
682  if (isList && me.isOpenClose) listEvenOdd();
683}
684
685/* Scrolls to a list item and highlights.
686 * @param me [oList, oToc] pane to change
687 * @param eA [HTMLAnchorElement]
688 */
689function paneScrollTo(me, eA) {
690  var eSTop,
691      offset,
692      eLI,
693      clkTop;
694
695  if (dbgListCalls)
696    console.log( "paneScrollTo " + (me === oList ? "LIST" : "TOC") );
697
698
699  if (eA) {
700    if (eLI = getPrntByIO(eA, HTMLLIElement) ) {
701      if (eLI !== me.clickedLI && (me.clickedLI instanceof HTMLLIElement) )
702        me.clickedLI.classList.remove(CN_CLICKED);
703
704      me.clickedLI = eLI;
705      me.clickedA  = eA;
706      if (me.isOpenClose) {
707        if (paneOpenToItem(eLI) && me === oList) listEvenOdd();
708      }
709      eLI.classList.add(CN_CLICKED);
710      offset = me.d.docked ? me.hdrHeight : winFloatingTop;
711      if (clickedBy === CB_OBJ_PATH)
712        clkTop = me.hdrHeight + 3 * H_CLS * px30em / 30.0;
713      else
714        clkTop = Math.max(me.hdrHeight, clickedTop) +  - fwHdrHgt;
715      eSTop = eLI.offsetTop - clkTop + offset;
716      me.nav.scrollTop = eSTop - ( me === oToc ? 1 : 0) - fwHdrHgt;
717    }
718  } else {
719    me.nav.scrollTop = 0;
720    if (me.clickedLI) me.clickedLI.classList.remove(CN_CLICKED);
721    me.clickedA  = null;
722    me.clickedLI = null;
723  }
724}
725
726/* Shows a pane
727 * @param [oList, oToc] me
728 */
729function paneShow(me) {
730  var isList = (me === oList),
731      you = isList ? oToc : oList,
732      curDocked = me.e.classList.contains(CN_DOCKED),
733      newDocked = me.d.docked,
734      isDockedChanged = (curDocked !== newDocked),
735      cnLoc = newDocked ? CN_DOCKED : CN_FLOATING;
736
737  if (isDockedChanged) me.e.className = cnLoc + (isTouch ? CN_TOUCH : '');
738
739  if (newDocked) {
740    me.s.width = ((winType === 1) ? cS.widthDocked : me.d.width) + 'px';
741  } else {
742    // me.s.width = '';
743    me.s.width = me.d.width + 'px';
744  };
745
746  me.e.classList.remove(CN_HIDDEN);
747  me.d.vis = true;
748  me.bVis.className = CN_BTN_VIS;
749
750  if ( !newDocked && !you.d.docked ) {
751    if (you.d.vis) paneHide(you);
752  } else if (winType === 1) {
753    if (newDocked && you.d.docked && you.d.vis) paneHide(you);
754  }
755  if (isList || aDOMCur.hash) me.showClicked(aDOMCur);
756  if (storage) storage.yardState = JSON.stringify(state);
757
758  updateCSS();
759};
760
761/* @!visibility public */
762
763//} @!endgroup
764
765//{ @!group oList Functions
766
767/* Event Handler for Window keyPress
768 * @param key [String] string of the key pressed
769 */
770function list_KeyPress(key) {
771  var eA,
772      re,
773      result;
774
775      // return if class, only list nested list
776  if (oListData[listCurPN].type === LIST_CLASS) return;
777
778  txt = key.replace(/([\?*+^|\.\$\[\]\(\)\{\}])/g, "\\$1");
779
780  if (oListData[listCurPN].type === LIST_METHOD) {
781    re = new RegExp('^[\.#]?' + txt, 'i');
782  } else {
783    re = new RegExp('^' + txt, 'i');
784  }
785
786  result = oListData[listCurPN].searchInfo.findIndex( function (o) { return re.test(o.text); } );
787
788  if (result) {
789    eA = oList.items.children[result];
790    oList.nav.scrollTop = getPrntByIO(eA, HTMLLIElement).offsetTop;
791  }
792}
793
794/* Loads List Pane.  Calls either xhr or cache load.
795 * @param url   [String]  url of list
796 * @param force [Boolean] force whether to always load page
797 */
798function list_Load(eA, force) {
799  var el = _id('list_items'),
800      url, pn;
801
802  if (force === undefined) force = false;
803
804  // return if base paths match
805  if ( !force && listPath === eA.pathname.replace(/[^\/]+$/, '') ) return;
806
807  if ( 'classList' in _id('list_search_icon') ) {
808    _id('list_search_icon').classList.remove('s_clr');
809  } else {
810    _id('list_search_icon').className = '';
811  };
812
813  if (dbgListCalls) console.log("list_Load\nlistCurPN\n" + listCurPN +
814    "\neA.pathname\n" + eA.pathname);
815
816  // save previous list state
817  if (listCurPN) {
818    if (oList.clickedLI) oList.clickedLI.classList.remove(CN_CLICKED);
819    oList.clickedLI = null;
820    oList.clickedA  = null;
821    oListData[listCurPN].list = el.cloneNode(true);
822    oListData[listCurPN].scrollTop = el.parentElement.scrollTop;
823  };
824
825  url = eA.href.trim();
826  pn  = eA.pathname;
827  oLoad.pathName = pn;
828
829  // new path, reset data and reload
830  if ( listPath !== pn.replace(/[^\/]+$/, '') ) {
831    oListData = {};
832    oList.clickedA = null;
833    oList.clickedLI = null;
834    listPath = pn.replace(/[^\/]+$/, '');
835    oLoad.xhr = xhrSend(url, listXhrCB);
836  } else if (force) {
837    setWait(CB_LIST);
838    // request has been cached, load from cache
839    if ( (oListData[pn] instanceof Object) &&
840         (oListData[pn].list instanceof HTMLUListElement) )
841      listXhrCB(true, '', url, 200, 0, 0)
842    // need to load from file
843    else oLoad.xhr = xhrSend(url, listXhrCB);
844  };
845};
846
847/* Highlights an item in the oList list
848 * @param aDOM [HTMLAnchorElement] anchor with object / url info
849 */
850function list_ShowClicked(aDOM) {
851  // aDOM hash is NOT encoded
852  var pathName = /^\//.test(aDOM.pathname) ? aDOM.pathname : '/' + aDOM.pathname,
853      pathFull = pathName,
854      eLI = oList.clickedLI,
855      hrefA,
856      qs,
857      eA,
858      eNewClickedLI,
859      paneUL,
860      t;
861
862  if (dbgListCalls) {
863    if (aDOM.hash)
864      console.log("list_ShowClicked\n" + pathName + '#' + t2EncodeStr( aDOM.hash.slice(1) ));
865    else
866      console.log("list_ShowClicked\n" + pathName);
867  };
868
869  if (clickedBy === CB_LIST) {
870    if (oList.clickedLIOld) {
871      oList.clickedLIOld.classList.remove(CN_CLICKED);
872      oList.clickedLIOld = null;
873    }
874    if (eLI) eLI.classList.add(CN_CLICKED);
875    return;
876  }
877
878  if (aDOM.hash) pathFull += '#' + t2EncodeStr( aDOM.hash.slice(1) );
879
880  if (oList.clickedA) {
881    hrefA = oList.clickedA.getAttribute('href');
882    if (hrefA === pathFull) {
883      eA = oList.clickedA;
884    } else if (hrefA === pathName) {
885      eA = oList.clickedA;
886      if ( eLI instanceof HTMLLIElement &&
887        eLI.classList.contains(CN_CLICKED) ) return;
888    };
889    if (eA) eNewClickedLI = eLI;
890  };
891
892  if (!eA) {
893    paneUL = ( t = _id('list_search_items') ) ? t : oList.items;
894    // Quotes should match template
895    qs = 'a[href="' + pathFull + '"]';
896    eA = paneUL.querySelector(qs);
897    if (!eA) {
898      qs = 'a[href="' + pathName + '"]';
899      eA = paneUL.querySelector(qs);
900    };
901  };
902
903  if (dbgListCalls) console.log("list_ShowClicked Found\n" + eA);
904
905  if (eA) paneScrollTo(oList, eA);
906  else {
907    if (oList.clickedLI) {
908      oList.clickedLI.classList.remove(CN_CLICKED);
909      oList.clickedLI = null;
910      oList.clickedA  = null;
911    };
912    if ( oListData[listCurPN] && oListData[listCurPN].scrollTop > 0)
913      oList.nav.scrollTop = oListData[listCurPN].scrollTop;
914  };
915};
916
917/* Event Handler for oList Header click */
918function listClkHeader(e) {
919  var tgt = e.target,
920      tag = tgt.tagName,
921      cancel = false;
922
923  settingsCheck();
924
925  switch (tag) {
926  case 'BUTTON':
927    if (tgt.id !== 'list_sizer')
928      paneOC(oList, (tgt.className === 'y_plus'));
929    cancel = true;
930    break;
931  case 'A':
932    if (!e.ctrlKey) {
933      cancel = true;
934      if (oLoad.xhr) {
935        oLoad.xhr.abort();
936        oLoad.xhr = null;
937      }
938      if (oLoad.isRunning) oLoad.cancel = true;
939      listSearchCancel();
940      list_Load(tgt, true);
941    }
942    break;
943  case 'path':
944    tag = 'svg';
945    tgt = tgt.parentElement;
946    /* falls through */
947  case 'svg':
948    if (tgt.id === 'list_search_icon') {
949      listSearchClear();
950      cancel = true;
951    }
952    break;
953  case 'INPUT':
954    if (tgt.id === 'list_search_text') {
955      tgt.focus();
956      cancel = true;
957    }
958    break;
959  }
960  if (cancel) {
961    e.stopPropagation();
962    e.preventDefault();
963    return false;
964  }
965}
966
967/* Sets the even / odd shading of oList items */
968function listEvenOdd(items) {
969  var liVis = [],
970        odd = [],
971       even = [],
972     lenVis = 0;
973
974  if (dbgListCalls) console.log("listEvenOdd\n" + listCurPN);
975
976  if (items === undefined) items = oList.items;
977
978  // array liVis passed by ref, loaded with visible items
979  listGetVis( items.children, liVis );
980  lenVis = liVis.length;
981  oList.itemsVis = lenVis;
982
983  // find all elements that require changing, push to odd & even arrays
984  for (var j = 0, el; j < lenVis; j++) {
985    el = liVis[j];
986    if (j % 2 === 0) {
987      if ( !el.classList.contains('odd') )  odd.push(el);
988    } else {
989      if ( !el.classList.contains('even')  ) even.push(el);
990    }
991  }
992
993  // now loop thru items in both arrays and change class
994  for (var j = 0, elCL, len =  odd.length; j < len; j++) {
995    elCL = odd[j].classList;
996    elCL.remove('even') ; elCL.add('odd');
997  }
998  for (var j = 0, elCL, len = even.length; j < len; j++) {
999    elCL = even[j].classList;
1000    elCL.remove('odd')  ; elCL.add('even');
1001  }
1002}
1003
1004/* Called after a new url is loaded into oList
1005 *
1006 * Sets the following:
1007 * * type
1008 * * isOpenClose
1009 *
1010 * @param [Object] ld oListData item.
1011 */
1012function listGetInfo(ld) {
1013  var ul   = ld.list,
1014      ulCL = ul.classList;
1015
1016  ld.type = LIST_UNKNOWN;
1017  for (var i = 0, t; t = LIST_ENUM[i]; i++) {
1018    if ( ulCL.contains(t) ) {
1019      ld.type = i;
1020      break;
1021    };
1022  };
1023  ld.isOpenClose = (ul.querySelector('button') !== null);
1024  if (dbgListTiming) oDbgList.elements = ul.querySelectorAll('*').length;
1025}
1026
1027/* Gets an ordered list of visible items (LI elements), only used when
1028 * isOpenClose is true.  Reentrant.
1029 * @param nl    [HTMLCollection] list of nodes to check
1030 * @param liVis [<HTMLLIElement>] array of LI elements that are visible
1031 */
1032function listGetVis(nl, liVis) {
1033  for (var i = 0, el, ul; el = nl[i]; i++) {
1034    liVis.push(el);
1035    ul = el.lastElementChild;
1036    if (ul instanceof HTMLUListElement && !el.classList.contains(CN_CLOSED)) {
1037      listGetVis( ul.children, liVis );
1038    }
1039  }
1040}
1041
1042/* Called when Ctrl-Alt combinations are used to goto a list
1043 * @param list [String]
1044 */
1045function listGoto(list) {
1046  var nl = _id('list_menu').querySelectorAll('a'),
1047      re = new RegExp('^\\s*' + list, 'i'),
1048      url = '', listState;
1049
1050  for (var i = 0, el; el = nl[i]; i++) {
1051    if (re.test(el.textContent)) {
1052      if (oLoad.xhr) {
1053        oLoad.xhr.abort();
1054        oLoad.xhr = null;
1055      }
1056      if   (oLoad.isRunning)   oLoad.cancel = true;
1057      listSearchCancel();
1058      list_Load(el, true);
1059      if (!oList.d.vis) paneShow(oList);
1060      break;
1061    }
1062  }
1063}
1064
1065/* Callback function needed for when {listLoadSplit} completes. */
1066function listLoadDone() {
1067  var eSearch = _id('list_search_text'),
1068      t;
1069
1070  if (dbgListCalls) console.log("listLoadDone");
1071
1072  oList.items = _id('list_items');
1073  oList.itemsSearch = 0;
1074
1075  if (isFirstLoad) {
1076    oList.s.visibility = '';
1077    clickedBy = CB_POP_STATE;
1078    clickedTop = (oToc.hdrHeight + 3.0 * H_CLS * px30em / 30) - fwHdrHgt;
1079    if (aDOMNext.hash)    gotoHash();
1080    else if (oList.d.vis) list_ShowClicked(aDOMNext);
1081    isFirstLoad = false;
1082  } else if (oList.d.vis) list_ShowClicked(aDOMNext);
1083
1084  if (dbgListCalls) console.log("listLoadDone\nlistCurPN\n" + listCurPN +
1085    "\noListData[listCurPN].scrollTop\n" + oListData[listCurPN].scrollTop);
1086
1087  if (listCurPN && oListData[listCurPN].scrollTop > 0) {
1088    if (!oList.clickedA || (oList.clickedA.href !== aDOMCur.href) )
1089      oList.nav.scrollTop = oListData[listCurPN].scrollTop;
1090  };
1091
1092  oLoad.isRunning = false;
1093  if (dbgListTiming) {
1094    oDbgList.render = performance.now() - oDbgList.render;
1095    dbgListLoad();
1096  }
1097
1098  if ( t = eSearch.value.trim()) {
1099    if (t !== '') listSearchChg();
1100  }
1101  setWait();
1102}
1103
1104/* Splits loading of LI elements when list is large, better UI responsiveness.
1105 * @param xhrEl    [HTMLElement] element with children to copy from (from xhr docFrag)
1106 * @param docEl    [HTMLElement] element to add children to (in document)
1107 * @param qty      [Integer]     number of children to load in the first loop
1108 * @param qty_next [Integer]     number of children to load in the rest of the loops
1109 * @param cbFunc   [Function]    callback function called when all children have
1110 *   been loaded/moved
1111 * @param oState   [object] object to pass run to app, and cancel from app
1112 */
1113function listLoadSplit(xhrEl, docEl, qty, qty_next, cbFunc, oState) {
1114  var func,
1115      tmrIds = [],
1116      rng = xhrEl.ownerDocument ? xhrEl.ownerDocument.createRange() : document.createRange(),
1117      docFrags = [];
1118
1119  setWait(CB_LIST);
1120  //var tSt = performance.now();
1121
1122  rng.setStart(xhrEl, 0);
1123  rng.setEnd(xhrEl, Math.min( qty, xhrEl.childElementCount) );
1124  docEl.appendChild( rng.extractContents() );
1125  qty = qty_next;
1126
1127  // load remaining nodes into docFrags array
1128  while (xhrEl.childElementCount > 0) {
1129    //console.log(xhrEl.firstChild.tagName + " len = " + xhrEl.childElementCount);
1130    rng.setStart(xhrEl, 0);
1131    rng.setEnd(xhrEl, Math.min(qty, xhrEl.childElementCount) );
1132    docFrags.push( rng.extractContents() );
1133  }
1134  rng.detach();
1135  rng = null;
1136  xhrEl = null;
1137  oState.isRunning = true;
1138  //console.log("Split items " + docFrags.length + " " + Math.round(performance.now() - tSt));
1139
1140  func = function() {
1141    //console.log("Timer " + Math.round(performance.now() - tSt));
1142    var frag;
1143    if (oState.cancel) {
1144      listClearLoadTimers(tmrIds, oState);
1145      docFrags = null;
1146    } else {
1147      if ( frag = docFrags.shift() ) docEl.appendChild(frag);
1148      frag = null;
1149      if (docFrags.length === 0) {
1150        oState.isRunning = false;
1151        if (cbFunc instanceof Function) setTimeout(cbFunc, 50);
1152      } else {
1153        tmrIds.push( setTimeout( func, 100) );
1154      };
1155    };
1156  };
1157  tmrIds.push( setTimeout( func, 15) );
1158};
1159
1160function listClearLoadTimers(ary, oState) {
1161  var len = ary.length;
1162  // console.log("called clearTimers " + len);
1163  if (len > 0) {
1164    for (var i = 0, tmr; tmr = ary[i]; i++) {
1165      clearTimeout(tmr);
1166    };
1167    ary = null;
1168  };
1169  oState.isRunning = false;
1170  setWait();
1171};
1172
1173/* xhr callback function when a new List document is ready, called by
1174 * xhrReadyStateChange
1175 * @param docFrag [DocumentFragment] body element of url document
1176 * @param title   [String] \<head\>\<title\> textContent
1177 * @param url     [String]
1178 * @param status  [Integer]
1179 * @param ms      [Float]   time to parse docFrag
1180 * @param msRcv   {Float]   time from xhrSend to xhrRSC
1181 */
1182function listXhrCB(docFrag, title, url, status, ms, msRcv) {
1183  var newMenu,
1184      newUL,
1185      oldUL = _id('list_items'),
1186      hdr   = _id('list_header'),
1187      ld,
1188      len,
1189      t, t1;
1190
1191  if (status !== 200 || !docFrag) {
1192    setWait();
1193    alert('listXhrCB - bad return with status ' + status + " from location\n" + url);
1194    return;
1195  }
1196
1197  oDbgList.xhrRSC = ms;
1198  var tSt = performance.now(), tEnd;
1199  oDbgList.xhrRcv = msRcv;
1200
1201  oLoad.xhr = null;
1202  oLoad.cancel = oLoad.isRunning;
1203  oList.clickedA = null;
1204  oList.clickedLI = null;
1205
1206  listCurPN  = oLoad.pathName;
1207
1208  if (oListData[listCurPN] === undefined) {
1209
1210    if (t = getDocFragId(docFrag, 'list_footer')) {
1211      elLibFooter = t.cloneNode(true);
1212      addFooter(document);
1213    } else {
1214      elLibFooter = null;
1215    }
1216
1217    oListData[listCurPN] = {};
1218    ld = oListData[listCurPN];
1219    ld.list = getDocFragId(docFrag, 'list_items').cloneNode(true);
1220    clean(ld.list);
1221    if ( t = getDocFragId(docFrag, 'list_menu') )
1222      ld.menu = t.cloneNode(true);
1223    else
1224      ld.menu = _id('list_menu').cloneNode(true);
1225    listGetInfo(ld);
1226
1227    ld.title = title;
1228    ld.search = '';
1229    listSearchLoad(ld);
1230    listEvenOdd(ld.list);
1231    docFrag = null;
1232  } else {
1233    ld = oListData[listCurPN];
1234  }
1235  oList.itemQty = ld.searchInfo.length;
1236  oList.isOpenClose = ld.isOpenClose;
1237
1238  newUL   = ld.list.cloneNode(true);
1239  newMenu = ld.menu.cloneNode(true);
1240
1241  // remove event listeners
1242  if ( t = _id('list_sizer') ) {
1243    t.removeEventListener('mousedown',  oList.resizeMD);
1244    if (isTouch)
1245      t.removeEventListener('touchstart', oList.resizeTS);
1246  };
1247
1248  _id('list_search_text').value = ld.search;
1249  if (ld.search === '') {
1250    if (t = _id('list_search_items') ) oList.nav.removeChild(t);
1251//    _id('list_search_icon').classList.remove('s_clr');
1252  } else {
1253    _id('list_search_icon').classList.add('s_clr');
1254//    newUL.style.display = 'none';
1255    len = 0;
1256  }
1257
1258  t = oList.isOpenClose;
1259  hdr.querySelector('button.y_minus').disabled =  !t;
1260  hdr.querySelector('button.y_plus' ).disabled =  !t;
1261
1262  _id('list_title').textContent = ld.title;
1263
1264  len = newUL.childElementCount;
1265
1266  if (dbgListTiming) {
1267    oDbgList.render = performance.now();
1268    oDbgList.cbProc = oDbgList.render - tSt;
1269    tSt = performance.now();
1270  }
1271
1272  hdr.replaceChild(newMenu, _id('list_menu') );
1273  // rebind events
1274  if ( t = _id('list_sizer') ) {
1275    t.addEventListener('mousedown',  oList.resizeMD, false);
1276    if (isTouch)
1277      t.addEventListener('touchstart', oList.resizeTS, false);
1278  };
1279
1280  if (len > 2000) {
1281    t = newUL.cloneNode(false);
1282    oList.nav.replaceChild(t, oldUL);
1283    oLoad.cancel = false;
1284    listLoadSplit(newUL, t, 1000, 4000, listLoadDone, oLoad);
1285  } else {
1286    setTimeout(listLoadDone, 15);
1287    oList.nav.replaceChild(newUL, oldUL);
1288  }
1289  newUl = null;
1290}
1291
1292//} @!endgroup
1293
1294//{ @!group oList Search Functions
1295
1296/* Event handler for oList search input change */
1297function listSearchChg(e) {
1298  var text = e ? e.target.value.trim() : _id('list_search_text').value.trim(),
1299      t;
1300
1301  if (text !== '') {
1302    // IE issue ?
1303    if ( 'classList' in _id('list_search_icon') ) {
1304      _id('list_search_icon').classList.add('s_clr');
1305    } else {
1306      _id('list_search_icon').className = 's_clr';
1307    };
1308    if (listLastSearchText !== text) listSearchCancel();
1309    listLastSearchText = text;
1310    listSearchExec(text);
1311  } else {
1312    listSearchClear();
1313  }
1314}
1315
1316/* Event handler for oList search input keypress */
1317function listSearchKP(e) {
1318  var key = String.fromCharCode(e.which);
1319  if ( !RE_LIST_SEARCH.test(key) ) {
1320    e.stopPropagation();
1321    return true;
1322  }
1323}
1324
1325function listSearchCancel() {
1326  for (var i = 0, o; o = oSearches[i]; i++) {
1327    if (o && o.running) o.cancel = true;
1328  }
1329}
1330
1331/* Clears List search list
1332 */
1333function listSearchClear() {
1334  var t;
1335  oList.itemsSearch = 0;
1336  _id('list_search_text').value = '';
1337  if ( 'classList' in _id('list_search_icon') ) {
1338    _id('list_search_icon').classList.remove('s_clr');
1339  } else {
1340    _id('list_search_icon').className = '';
1341  };
1342  oListData[listCurPN].search = '';
1343  setWait(CB_LIST);
1344  setTimeout( function() {
1345    listSearchCancel();
1346    if (t = _id('list_search_items') ) oList.nav.removeChild(t);
1347    oList.items.style.display = '';
1348    oList.clickedA  = null;
1349    oList.clickedLI = null;
1350    if (clickedBy === CB_LIST) clickedBy = null;
1351    if (aDOMCur) list_ShowClicked(aDOMCur);
1352    setWait();
1353  }, 10);
1354}
1355
1356/* Create elements from search result array
1357 * @param list [<object>]
1358 * @param re   [RegEx] regex string for highlighting search result text
1359 */
1360function listSearchCreate(list, re) {
1361  var li = document.createElement('li'),
1362      docFrag = document.createDocumentFragment(),
1363      a, findText, replText, inHTML,
1364      emptyRe = re.test('');
1365
1366  for (var i = 0, sObj, small, newLI; sObj = list[i]; i++) {
1367    newLI = li.cloneNode(false);
1368
1369    a = sObj.a.cloneNode(true);
1370    findText = a.firstChild.textContent;
1371//    txt = findText.replace(/&/g, '&amp;');
1372//    txt = txt.replace(/</g, '&lt;');
1373    txt = findText.replace(/</g, '&lt;');
1374    txt = txt.replace(/>/g, '&gt;');
1375
1376    replText = txt.replace(re, "<em>$1</em>");
1377
1378    if (!emptyRe) {
1379      a.innerHTML = a.textContent.replace(findText, replText);
1380    };
1381
1382    newLI.appendChild(a);
1383    if (sObj.small) {
1384      small = sObj.small.cloneNode(true);
1385      small.className = '';
1386      small.textContent = small.textContent.trim();
1387      newLI.appendChild( small );
1388    }
1389    if (sObj.cl) {
1390      newLI.className = sObj.cl;
1391      if (i % 2 === 0) newLI.classList.add('odd');
1392      else             newLI.classList.add('even');
1393    } else {
1394      if (i % 2 === 0) newLI.className = 'odd';
1395      else             newLI.className = 'even';
1396    }
1397    docFrag.appendChild(newLI);
1398  }
1399  return docFrag;
1400}
1401
1402/* Performs the List search
1403 * @note If result set is large, calls {listLoadSplit} so UI is responsive
1404 * while loading search results.  This allows search input text changes to
1405 * cancel the search result display.
1406 * @param text [String] text string to search for
1407 */
1408function listSearchExec(text) {
1409  var re,
1410      results,
1411      ul = document.createElement('ul'),
1412      oldUl,
1413      txt, pre, t,
1414      isStart,
1415      reNS,
1416      ld = oListData[listCurPN],
1417      tSt = performance.now(), tS;
1418
1419  text = text.replace(/\s+/g, '');
1420  ld.search = text;
1421
1422
1423  if ( t2Info.NSEP === '::' && text.match(/[a-z][\.#]\S/i) ) {
1424    aText = text.split(/[\.#]/);
1425  } else {
1426    aText = text.split(t2Info.NSEP);
1427  }
1428  if (aText.length === 2) {
1429    reNS = new RegExp(aText[0]);
1430    text = aText[1];
1431  } else if (aText.length > 2) {
1432    text = aText.pop();
1433    reNS = new RegExp( aText.join("([^:]*" + t2Info.NSEP + "[^:]*)+?") );
1434  } else {
1435    reNS = null;
1436  };
1437
1438  pre = text.charAt(0);
1439  if (pre === t2Info.CSEP || pre === t2Info.ISEP) {
1440    text = text.slice(1);
1441    if (pre === '.') pre = '\\.';
1442  } else pre = null;
1443
1444  // isStart = ( /^[^!=?<>+|*\^\]_]/.test(text.charAt(0)) && (
1445  //    text.charAt(0) === text.charAt(0).toUpperCase() ) );
1446  isStart = /[A-Z]/.test(text.charAt(0));
1447
1448
1449  // create RegEx for search, dependent on whether 1st letter is uppercase
1450  // escapes ? * + ^ | . $ [ ] ( ) { } in search string
1451  txt = text.replace(/([\?*+^|\.\$\[\]\(\)\{\}])/g, "\\$1");
1452  if (isStart) {
1453    if (ld.type === LIST_METHOD) {
1454      if (pre)
1455        re = new RegExp('^' + pre + txt, 'i');
1456      else
1457        re = new RegExp('(^#' + txt + '|^\\.' + txt + '|^' + txt + ')', 'i');
1458    } else
1459      re = new RegExp('^' + txt, 'i');
1460  } else {
1461    if (ld.type === LIST_METHOD && pre ) {
1462      re = new RegExp('^' + pre + '.*?' + txt, 'i');
1463    } else {
1464      re = new RegExp(txt, 'i');
1465    }
1466  }
1467  if (reNS) {
1468    results = ld.searchInfo.filter( function (o) { return (re.test(o.text) &&
1469      !!o.small && reNS.test(o.small.textContent)); } );
1470  } else {
1471    results = ld.searchInfo.filter( function (o) { return re.test(o.text); } );
1472  }
1473
1474  oList.itemsSearch = results.length;
1475
1476  if (oList.itemsSearch > 0) {
1477    // sort objects via name then namespace
1478    if (oList.isOpenClose) {
1479      results = results.sort( function(a, b) {
1480        var at = a.text.toLowerCase(),
1481            bt = b.text.toLowerCase(),
1482            as = a.small ? a.small.textContent : '',
1483            bs = b.small ? b.small.textContent : '';
1484
1485        if (at > bt)      return  1;
1486        else if (at < bt) return -1;
1487        else if (as > bs) return  1;
1488        else if (bs > as) return -1;
1489        else return 0;
1490      } );
1491    }
1492
1493    if (ld.type === LIST_UNKNOWN || ld.type === LIST_FILE)
1494      ul.className = 'file';
1495    else
1496      ul.className = LIST_ENUM[LIST_METHOD];
1497
1498    // Create RegEx (re) for search string highlight
1499//    txt = txt.replace(/&/g, '&amp;');
1500    txt = txt.replace(/</g, '&lt;');
1501    txt = txt.replace(/>/g, '&gt;');
1502    if (isStart) {
1503      if (oListData[listCurPN].type === LIST_METHOD) {
1504        if (pre)
1505          re = new RegExp('^(' + pre + txt + ')', 'i');
1506        else
1507          re = new RegExp('^([#\.]?' + txt + ')', 'i');
1508      } else
1509        re = new RegExp('^(' + txt + ')', 'i');
1510    } else {
1511      re = new RegExp('(' + txt + ')', 'ig');
1512    }
1513  }
1514
1515  if (dbgSearchLoad) {
1516    tS = fmtTL(performance.now() - tSt, oList.itemsSearch);
1517    console.log('Search   exec    ' + tS + ' ' + text);
1518  }
1519
1520  oList.items.style.display = 'none';
1521  ul.id = 'list_search_items';
1522  oldUl = _id('list_search_items');
1523  // If less than 600 matches, throw them all in, otherwise use splitLoad
1524  if (oList.itemsSearch < 600) {
1525    ul.appendChild( listSearchCreate(results, re) );
1526    if (oldUl)
1527      oList.nav.replaceChild(ul, oldUl);
1528    else
1529      oList.nav.appendChild(ul);
1530    listSearchExec2();
1531  } else {
1532    var   sUL = ul.cloneNode(false),
1533      oSearch = {
1534          list: results,
1535            re: re,
1536            ul: sUL,
1537           qty: 200,
1538          text: text,
1539       running: true,
1540        cancel: false,
1541            cb: listSearchExec2
1542      };
1543    oSearches.push(oSearch);
1544    if (oldUl) oList.nav.replaceChild(sUL, oldUl);
1545    else       oList.nav.appendChild(sUL);
1546    listSearchSplit(oSearch);
1547  }
1548  t = null;
1549}
1550
1551/* Callback function needed for when search display {listLoadSplit} completes. */
1552function listSearchExec2() {
1553  var len = oSearches.length - 1,
1554      o;
1555  if (oList.itemsSearch > 0) {
1556    oList.clickedA = null;
1557    oList.showClicked(aDOMCur);
1558    if (!oList.clickedLI) oList.nav.scrollTop = 0;
1559  }
1560  if (len >= 0) {
1561    for (var i = len; i >= 0; i--) {
1562      o = oSearches[i];
1563      if (o && !o.running) {
1564        o = null;
1565        oSearches.pop();
1566      }
1567    }
1568  }
1569}
1570
1571/* Splits loading of LI elements when list is large, better UI responsiveness.
1572 * @note Used for both large (+2,000) lists and large (+500) search result sets.
1573 * @param xhrEl [HTMLElement] element with children to copy from (from xhr frag)
1574 * @param docEl [HTMLElement] element to add children to (in document)
1575 * @param qty [Integer] number of children to load in each loop
1576 * @param cbFunc [Function] callback function called when all children have
1577 * been loaded/moved
1578 * @param oState [object] object to pass run to app, and cancel from app
1579 */
1580function listSearchSplit(o) {
1581  var tmrFunc,
1582      tmrIds = [],
1583      list,
1584      len;
1585
1586  var tSt = performance.now(), tS;
1587
1588  tmrFunc = function() {
1589    return function() {
1590      if (o.list.length > 0) {
1591        list = o.list.splice(0, o.qty);
1592        o.ul.appendChild( listSearchCreate(list, o.re) );
1593        if (!o.cancel) {
1594          if (o.list.length > 0) {
1595            if (dbgSearchSplit) {
1596              tS = fmtTL(performance.now() - tSt, o.list.length + 0);
1597              console.log('Search   split   ' + tS + ' ' +
1598                LIST_ENUM[oListData[listCurPN].type]  + '  ' + o.text);
1599            }
1600            tmrIds.push(setTimeout(tmrFunc, 20));
1601          } else {
1602            o.running = false;
1603            if (o.cb !== undefined) setTimeout(o.cb, 0);
1604            return;
1605          }
1606        } else {
1607          len = tmrIds.length;
1608          if (len > 0) {
1609            for (var i = 0; i < len; i++) {
1610              if (tmrIds[i]) clearTimeout(tmrIds[i]);
1611            }
1612          }
1613          setWait(-1);
1614          o.running = false;
1615          if (dbgSearchSplit) {
1616            tS = fmtTL(performance.now() - tSt, o.list.length);
1617            console.log('Search   split   ' + tS + ' ' +
1618              LIST_ENUM[oListData[listCurPN].type] + ' Cancel');
1619          }
1620        }
1621      }
1622    };
1623  }();
1624  tmrIds.push(setTimeout(tmrFunc, 0));
1625}
1626
1627/* Loads ld.searchInfo array, sets all href's to the correct path
1628 * @param ul   [HTMLUListElement]  the top UL element to parse
1629 * @param menu [HTMLDivElement] the list menu element
1630 */
1631function listSearchLoad(ld) {
1632  var nl  = ld.list.querySelectorAll('a'),
1633      cl,
1634      tSt = performance.now(), tEnd;
1635
1636  ld.searchInfo = [];
1637  // Load searchInfo and set list href's
1638  if (isServer) {
1639    for (var i = 0, eA, sObj, _small, eLI; eA = nl[i]; i++) {
1640      _small = eA.nextElementSibling;
1641      if (_small && _small.tagName === 'SMALL')
1642        sObj = { a: eA, small: _small, text: eA.childNodes[0].textContent };
1643      else {
1644        sObj = { a: eA, small: null  , text: eA.childNodes[0].textContent };
1645
1646      }
1647      if (eLI = getPrntByIO(eA, HTMLLIElement)) {
1648        cl = eLI.className.replace(/even|odd/, '').trim();
1649        if (cl !== '') sObj.cl = cl;
1650      }
1651      if ( eA.classList.contains('nodoc') ) sObj.cl += ' nodoc';
1652      ld.searchInfo.push(sObj);
1653    }
1654  } else {
1655    for (var i = 0, eA, sObj, _small, eLI; eA = nl[i]; i++) {
1656      eA.setAttribute('href', listPath + eA.getAttribute('href') );
1657      _small = eA.nextElementSibling;
1658      if (_small && _small.tagName === 'SMALL')
1659        sObj = { a: eA, small: _small, text: eA.childNodes[0].textContent };
1660      else {
1661        sObj = { a: eA, small: null  , text: eA.childNodes[0].textContent };
1662      };
1663      if (eLI = getPrntByIO(eA, HTMLLIElement)) {
1664        cl = eLI.className.replace(/even|odd/, '').trim();
1665        if (cl !== '') sObj.cl = cl;
1666      };
1667      if ( eA.classList.contains('nodoc') ) sObj.cl += ' nodoc';
1668      ld.searchInfo.push(sObj);
1669    };
1670    // Set list menu href's
1671    nl = ld.menu.querySelectorAll('a');
1672    for (var i = 0, eA; eA = nl[i]; i++) {
1673      eA.setAttribute('href', listPath + eA.getAttribute('href') );
1674    };
1675  };
1676
1677  if (dbgSearchLoad) {
1678    tEnd = performance.now() - tSt;
1679    console.log('Search   load    ' + fmtTL(tEnd, oList.itemQty) +
1680            ' ' + LIST_ENUM[ld.type]);
1681  }
1682}
1683
1684//} @!endgroup
1685
1686//{ @!group oToc Functions
1687
1688/* Generates a table of contents and returns the new OL element
1689 * @param content [HTMLElement] the node containg the doc content
1690 * @param isCode  [Boolean] true is the doc is a 'code' doc
1691 * @return [HTMLOLElement] an OL element filled with TOC info
1692 */
1693function toc_Generate(content, isCode) {
1694  var cA   = document.createElement('a'),
1695      cBTN = document.createElement('button'),
1696      cDIV = document.createElement('div'),
1697      cLI  = document.createElement('li'),
1698      cOL  = document.createElement('ol'),
1699      a, div, li, ol,
1700      _toc = cOL.cloneNode(false),
1701      toc = _toc,
1702      counter = 0,
1703      tags = ['h2', 'h3', 'h4', 'h5', 'h6'],
1704      level = 0,
1705      nl,
1706      hTags = [],
1707      proposedId,
1708      qs,
1709      lastLvl,
1710      prntIsH2Details,
1711      thisLvl,
1712      nextLvl,
1713      thisIsA,
1714      thisIsH2Details,
1715      nextIsA,
1716      title,
1717      tmp, tmp1,
1718      itemHref,
1719      elCL,
1720      cls,
1721      leaveOpenLvl = 2;
1722
1723  oToc.clickedA = null;
1724  oToc.clickedLI = null;
1725  if (content.childNodes === 0) return;
1726
1727  if (isCode) tocSearchLoad(content);
1728
1729  cDIV.appendChild( cBTN.cloneNode(false) );
1730
1731  _toc.id = 'toc_items';
1732  _toc.className = 'class lvl0';
1733
1734  tmp = content.querySelectorAll('h1').length -
1735        content.querySelectorAll('h1.title').length -
1736        content.querySelectorAll('h1.alphaindex').length;
1737
1738  if (tmp > 1) {
1739    tags.unshift('h1');
1740    leaveOpenLvl = 1;
1741  }
1742
1743  lastLvl = parseInt(tags[0][1], 10);
1744//  tmp = tags.join(', ') + (isCode ? ', ul.summary.compact:not(.constants) a' : '');
1745  tmp = tags.join(', ');
1746  nl = content.querySelectorAll(tmp);
1747  // load nl into array hTags
1748  for (var i = 0, el; el = nl[i]; i++) { hTags.push(el); };
1749
1750  // now, loop array
1751  for (var i = 0, el; el = hTags[i]; i++) {
1752    thisIsA = (isCode && el instanceof HTMLAnchorElement)
1753
1754//      if (getPrntByIO(el, HTMLUListElement, true).classList.contains('constants'))
1755//        continue;
1756
1757    thisLvl = thisIsA ? 3 : parseInt(el.tagName[1], 10);
1758
1759    thisIsH2Details = (isCode && thisLvl === 2 &&
1760      (el.classList.contains('y_details') || el.textContent.match(/Details$/i)));
1761
1762    if ( !thisIsH2Details && prntIsH2Details &&
1763      !el.classList.contains('signature') ) {
1764        continue;
1765    }
1766
1767    if (isCode && !thisIsA && thisLvl > 3 &&
1768        (getPrntByIO(el, HTMLLIElement) instanceof HTMLLIElement)) {
1769      continue;
1770    }
1771
1772//console.log(thisLvl + ' ' + (thisIsA ? 'T' : 'F') + ' ' +
1773//  ( (getPrntByIO(el, HTMLLIElement) instanceof HTMLLIElement) ? 'LI ' : '   ') + el.textContent);
1774
1775    // nextLvl info, skip if in Code Details
1776    if (hTags[i+1] && (!isCode || (thisIsH2Details || !prntIsH2Details) )) {
1777//      if (hTags[i+1] && (!isCode || (thisIsH2Details && !prntIsH2Details) )) {
1778      tmp = hTags[i+1];
1779      if (isCode && tmp instanceof HTMLAnchorElement) {
1780        nextIsA = true;
1781        nextLvl = 3;
1782      } else if (isCode && !(getPrntByIO(tmp, HTMLLIElement) instanceof HTMLLIElement)) {
1783        nextLvl = parseInt(tmp.tagName[1], 10);
1784      } else if (!isCode) nextLvl = parseInt(tmp.tagName[1], 10);
1785    } else nextLvl = thisLvl;
1786
1787    // need something for rendered objects in md files ??
1788
1789    if (thisIsA) {
1790      // el is a 'summary.compact a', no children, title already parsed
1791      a = el.cloneNode(false);
1792      a.textContent = el.textContent;
1793      li = cLI.cloneNode(false);
1794    } else {
1795      // Get text for toc entry
1796      if (isCode) {
1797        title = tocCodeTitle(el, content, cls);
1798        cls = '';
1799        elCL = el.classList;
1800        if (thisLvl === 3 && elCL.contains('signature')) {
1801              // set up classes for method type indicators
1802          if (el.querySelector('strong.nodoc, div.note.nodoc') ||
1803              el.classList.contains('nodoc'))
1804//              (el.nextElementSibling &&
1805//               el.nextElementSibling.classList.contains('nodoc')) )
1806                                                  cls += 'nodoc ';
1807          if (el.parentElement.querySelector('div.note.deprecated'))
1808                                                  cls += 'deprecated ';
1809          if (el.querySelector('span.readonly'))  cls += 'ro ';
1810          if (elCL.contains('rw'))                cls += 'rw ';
1811          if (el.querySelector('span.writeonly')) cls += 'wo ';
1812          if (el.querySelector('span.priv'))      cls += 'priv ';
1813          if (el.querySelector('span.prot'))      cls += 'prot ';
1814          if (el.querySelector('span.mod_func'))  cls += 'mf ';
1815          cls = cls.trim();
1816        }
1817      } else {
1818        title = el.getAttribute('toc-title') || el.textContent;
1819      }
1820      // Set id on h tag if undefined
1821      if (isCode && thisLvl === 3 && el.parentElement.tagName === 'SECTION') {
1822        itemHref = el.parentElement.id;
1823      } else {
1824        itemHref = el.id;
1825      }
1826      if (itemHref.length === 0) {
1827        proposedId = el.getAttribute('toc-id') || el.textContent.toLowerCase().replace(/& /g, '-').replace(/[^a-z0-9-]+/g, '-');
1828        qs = /^[0-9]/.test(proposedId) ? '#\\' : '#';
1829        if ( /^-/.test(proposedId) ) proposedId = "_" + proposedId;
1830        if (proposedId === null || proposedId === '' ||
1831            content.querySelector(qs + proposedId) ) {
1832          proposedId = title.toLowerCase().replace(/& /g, '-').replace(/[^a-z0-9-]+/g, '-');
1833          // selectors can't start with an unescaped number
1834          if ( /\s*/.test(proposedId) ) proposedId = "_" + counter; counter++;
1835          qs = /^[0-9]/.test(proposedId) ? '#\\' : '#';
1836          if ( content.querySelector(qs + proposedId) )
1837            proposedId += counter; counter++;
1838        }
1839        el.id = proposedId;
1840        itemHref = proposedId;
1841
1842      }
1843      // load a with title & href
1844      a = cA.cloneNode(false);
1845      if (cls !== '' && cls !== undefined) a.className = cls;
1846      a.setAttribute('href','#' + itemHref);
1847
1848      if ( el.classList.contains('inherited') && /ed$/.test(title) && / - /.test(title) ) {
1849        tmp = document.createTextNode(/^\S+/.exec(title) + ' - ');
1850        a.appendChild(tmp);
1851        tmp = document.createElement("I");
1852        tmp.textContent = /\S+$/.exec(title);
1853        a.appendChild(tmp);
1854      } else a.textContent = title;
1855
1856      // move back up
1857      if (thisLvl < lastLvl && toc) {
1858        for (var k = 0, newToc; k < lastLvl - thisLvl; k++) {
1859          level --;
1860          toc = getPrntByIO(toc, HTMLOListElement, true);
1861        }
1862      }
1863    }
1864    li = cLI.cloneNode(false);
1865
1866    if (thisLvl < nextLvl) {
1867      // Has children, needs buttons, div, ol, etc
1868      level ++
1869      if (thisLvl > leaveOpenLvl) li.className = CN_CLOSED;
1870
1871      div = cDIV.cloneNode(true);
1872      div.appendChild(a);
1873      li.appendChild(div);
1874
1875      ol  =  cOL.cloneNode();
1876      ol.className = 'lvl' + level;
1877      li.appendChild(ol);
1878      for (var k = thisLvl + 1, t; k < nextLvl; k++) {
1879        level++;
1880        t = cOL.cloneNode();
1881        t.className = 'lvl' + level;
1882        ol.appendChild(t);
1883        ol = t;
1884      }
1885      if (toc) toc.appendChild(li);
1886      toc = ol;
1887    } else {
1888      li.appendChild(a);
1889//      if (cls !== '') li.className = cls;
1890//      toc.appendChild(li);
1891      if (toc) toc.appendChild(li);
1892    }
1893    lastLvl = thisLvl;
1894    if (isCode)
1895      prntIsH2Details = prntIsH2Details || thisIsH2Details;
1896  }
1897
1898  // Done with toc, check for summary vs detail children, and maybe remove some
1899  //if (isCode) tocRemoveSummaryChildren(_toc);
1900
1901  oToc.isOpenClose = (_toc.querySelector('button') !== null);
1902//  oToc.nav.replaceChild(_toc, oToc.items);
1903//  oToc.items = _id('toc_items');
1904  return _toc;
1905}
1906
1907/* Highlights an item in the TOC pane
1908 * @param aDOM [HTMLAnchorElement] anchor with object / url info,
1909 * only the hash is used
1910 */
1911function toc_ShowClicked(aDOM) {
1912  var qs = 'a[href="' + decodeURIComponent(aDOM.hash.replace(/%-/, '%25-')) + '"]',
1913      eA = oToc.items.querySelector(qs);
1914
1915  if (clickedBy === CB_TOC) {
1916    if (oToc.clickedLIOld) {
1917      oToc.clickedLIOld.classList.remove(CN_CLICKED);
1918      oToc.clickedLIOld = null;
1919    }
1920    if (oToc.clickedLI) oToc.clickedLI.classList.add(CN_CLICKED);
1921    return;
1922  }
1923  paneScrollTo(oToc, eA);
1924};
1925
1926/* Event handler for TOC header click */
1927function tocClkHeader(e) {
1928  var tgt = e.target,
1929      tag = tgt.tagName,
1930      cancel = false;
1931
1932  settingsCheck();
1933  switch (tag) {
1934  case 'BUTTON':
1935    if (tgt.id !== 'toc_sizer')
1936      paneOC(oToc, (tgt.className === 'y_plus'));
1937    cancel = true;
1938    break;
1939  case 'path':
1940    tag = 'svg';
1941    tgt = tgt.parentElement;
1942    /* falls through */
1943  case 'svg':
1944    if (tgt.id === 'toc_search_icon') {
1945      tocSearchClear();
1946      cancel = true;
1947    }
1948    break;
1949  }
1950  if (cancel) {
1951    e.stopPropagation();
1952    e.preventDefault();
1953    return false;
1954  }
1955}
1956
1957/* Used with code documents, generates a table of contents entry from an element
1958 * @param el [HTMLElement] element to extract title from
1959 * @return [String] TOC title / entry
1960 */
1961function tocCodeTitle(el) {
1962  var title = ( el.getAttribute('toc-title') || el.textContent).trim(),
1963      thisLvl = parseInt(el.tagName[1], 10),
1964      rePlus = /^[\+\*\|\^]/,
1965      re,
1966      titleMain,
1967      titleSuf,
1968      pre = '',
1969      prnt,
1970      prntId,
1971      t;
1972
1973  if (thisLvl === 2) {
1974    // removes small text, typically expand / collapse
1975    if (t = el.querySelector('small')) {
1976      titleSuf = t.textContent;
1977      if (titleSuf) {
1978        title = title.replace(titleSuf, '').trim();
1979      }
1980    }
1981    title = title.replace(/Attributes & Methods/, 'A & M');
1982  } else if (thisLvl === 3) {
1983    if ( el.classList.contains('signature') ) {
1984      if ( el.classList.contains('aws_waiter') ) {
1985        title = el.querySelector('strong').textContent.replace(/'/g, '');
1986      } else {
1987        // check id of section element
1988        prnt = el.parentElement;
1989        if (prnt.tagName === 'SECTION' &&
1990            prnt.classList.contains('method_details') ) {
1991          prntId = prnt.id;
1992
1993          if ( t = el.querySelector('strong') ) {
1994            if (t.previousSibling && t.previousSibling.nodeType === 3 )
1995              pre = t.previousSibling.nodeValue.trim();
1996          }
1997
1998          if ( RE_METH_CLS.test(prntId) )
1999            title = pre + prntId.replace(RE_METH_CLS, '');
2000          else if ( RE_METH_INST.test(prntId) )
2001            title = pre + prntId.replace(RE_METH_INST, '');
2002
2003//            title = t2Info.CSEP + prntId.replace(RE_METH_CLS, '');
2004//            title = t2Info.ISEP + prntId.replace(RE_METH_INST, '');
2005
2006          if (title) return title.replace(/[\(\s].*$/, '');
2007        }
2008
2009        if (t = el.querySelector('small')) {
2010          title = title.replace(t.textContent, '').trim();
2011        }
2012        title = title.replace(/[( ][\s\S]*$/g, '');
2013        if (t = el.querySelector('strong')) {
2014          titleMain = t.textContent.replace(/\?/, "\\?");
2015          if ( rePlus.test(titleMain) )
2016            re = new RegExp('[#.]?\\' + titleMain);
2017          else
2018            re = new RegExp('[#.]?' + titleMain);
2019          if ( t = title.match(re) ) title = t[0];
2020        }
2021      }
2022    } else if ( el.classList.contains('inherited') ) {
2023      title = title.replace(/^.*?(inherited|included)[ ]+from/, "<i>$1 -");
2024    }
2025  }
2026  title = title.trim();
2027  return title;
2028}
2029
2030/* Removes children from 'Summary' entries if they are similar to the 'Details'
2031 * entries
2032 * @param toc [HTMLOLElement]
2033 */
2034function tocRemoveSummaryChildren(toc) {
2035  var RE_SUMMARY = / Summary$/,
2036      RE_DETAILS = / Details$/,
2037      summaries = [],
2038      details   = [],
2039      checked,
2040      nl, i, j, txtS, txtD, el, elS, elD, qtyS, qtyD, a, t;
2041
2042  nl = toc.childNodes;
2043  for (i = 0; el = nl[i]; i++) {
2044    txtS = el.firstElementChild.textContent;
2045    // push all entries that end in SUMMARY or DETAILS to arrays
2046    if (RE_SUMMARY.test(txtS)) summaries.push(el);
2047    if (RE_DETAILS.test(txtS)) details.push(el);
2048  }
2049  // loop thru summaries and look for matching details
2050  for (i = 0; elS = summaries[i]; i++) {
2051    checked = false;
2052    txtS = elS.firstElementChild.textContent.replace(RE_SUMMARY, '');
2053    for (j = 0; elD = details[j]; j++) {
2054      txtD = elD.firstElementChild.textContent.replace(RE_DETAILS, '');
2055      if (txtD === txtS) {
2056        nl = elS.querySelectorAll('ol > li');
2057        qtyS = nl.length - elS.querySelectorAll('a > i').length;
2058        qtyD = elD.querySelectorAll('ol > li').length;
2059
2060        if (qtyS + 1 >= qtyD) {
2061          if ( elS.querySelector('a > i') ) {
2062            console.log('Remove ' + txtS);
2063            nl = elS.querySelectorAll('li');
2064            console.log(txtS + ' ' + nl.length);
2065            for (var k = 0, el; el = nl[k]; k++) {
2066              if ( !el.querySelector('a > i') )
2067                el.parentElement.removeChild(el);
2068            }
2069          } else {
2070            if ( t = elS.querySelector('ol') ) elS.removeChild(t);
2071            a = elS.querySelector('a').cloneNode(true);
2072            if (t = elS.querySelector('div'))
2073              elS.removeChild(t);
2074            elS.className = '';
2075            elS.appendChild(a);
2076          }
2077        }
2078        // break because a match was found
2079        break;
2080        checked = true;
2081      }
2082    }
2083    // Probably a constructor in class methods
2084    if (!checked && elS.querySelectorAll('ol > li').length === 1) {
2085      elS.removeChild(elS.querySelector('ol'));
2086      a = elS.querySelector('a').cloneNode(true);
2087      elS.removeChild(elS.querySelector('div'));
2088      elS.className = '';
2089      elS.appendChild(a);
2090    }
2091  }
2092}
2093
2094//} @!endgroup
2095
2096//{ @!group oToc Search Functions
2097
2098/* Event handler for oList search input keypress */
2099function tocSearchKP(e) {
2100  var key = String.fromCharCode(e.which);
2101  if ( !RE_LIST_SEARCH.test(key) ) {
2102    e.stopPropagation();
2103    return true;
2104  }
2105}
2106
2107function tocSearchCancel() {
2108  // console.log('tocSearchCancel');
2109};
2110
2111/* Event handler for oTOC search input change */
2112function tocSearchChg(e) {
2113  var text = e ? e.target.value.trim() : _id('toc_search_text').value.trim(),
2114      t;
2115
2116  if (text !== '') {
2117    _id('toc_search_icon').classList.add('s_clr');
2118    if (tocLastSearchText !== text) {
2119      tocSearchCancel();
2120    }
2121    tocLastSearchText = text;
2122    tocSearchExec(text);
2123  } else {
2124    tocSearchClear();
2125  }
2126}
2127
2128/* Clears Toc search list
2129 */
2130function tocSearchClear() {
2131  var t;
2132  //oList.itemsSearch = 0;
2133  _id('toc_search_text').value = '';
2134  _id('toc_search_icon').classList.remove('s_clr');
2135  setWait(CB_LIST);
2136  setTimeout( function() {
2137    if (t = _id('toc_search_items') ) oToc.nav.removeChild(t);
2138    oToc.items.style.display = '';
2139    oToc.clickedA  = null;
2140    oToc.clickedLI = null;
2141    if (aDOMCur) toc_ShowClicked(aDOMCur);
2142    setWait();
2143  }, 10);
2144}
2145
2146/* Creates the html DocFrag of search results
2147 * @param [array] array of search matches
2148 * @param [re]    regex for text highlighting
2149 * @return [DocumentFragment]
2150 */
2151function tocSearchCreate(list, re) {
2152  var li = document.createElement('li'),
2153      aa = document.createElement('a'),
2154      ss = document.createElement('small'),
2155      docFrag = document.createDocumentFragment(),
2156      a, sml,
2157      findText, replText, inHTML;
2158
2159  for (var i = 0, sObj, newLI; sObj = list[i]; i++) {
2160    newLI = li.cloneNode(false);
2161    a = aa.cloneNode(false);
2162
2163    findText = sObj.text;
2164    txt = findText.replace(/&/g, '&amp;');
2165    txt = txt.replace(/</g, '&lt;');
2166    txt = txt.replace(/>/g, '&gt;');
2167
2168    sml = ss.cloneNode(false);
2169    sml.textContent = sObj.small;
2170
2171    a.innerHTML = txt.replace(re, "<em>$1</em>");
2172    a.href = sObj.href;
2173    newLI.className = sObj.cls;
2174
2175    newLI.appendChild(a);
2176    newLI.appendChild(sml);
2177
2178    docFrag.appendChild(newLI);
2179  }
2180  return docFrag;
2181}
2182
2183/* Performs the TOC search
2184 * @param text [String] text string to search for
2185 */
2186function tocSearchExec(text) {
2187  var re,
2188      results,
2189      ul = document.createElement('ul'),
2190      oldUl,
2191      txt, pre, t,
2192      isStart,
2193      reNS,
2194      tSt = performance.now(), tS;
2195
2196  text = text.replace(/\s+/g, '');
2197
2198  pre = text.charAt(0);
2199  if (pre === t2Info.CSEP || pre === t2Info.ISEP) {
2200    text = text.slice(1);
2201    if (pre === '.') pre = '\\.';
2202  } else pre = null;
2203
2204  isStart = /[A-Z]/.test(text.charAt(0));
2205
2206  // create RegEx for search, dependent on whether 1st letter is uppercase
2207  // escapes ? * + ^ | . $ [ ] ( ) { } in search string
2208  txt = text.replace(/([\?*+^|\.\$\[\]\(\)\{\}])/g, "\\$1");
2209  if (isStart) {
2210    if (pre)
2211      re = new RegExp('^' + pre + txt, 'i');
2212    else
2213      re = new RegExp('(^' + re_csep + txt + '|^' + re_isep + txt + '|^' + txt + ')', 'i');
2214  } else {
2215    if (pre)
2216      re = new RegExp('^' + pre + '.*?' + txt, 'i');
2217    else
2218      re = new RegExp(txt, 'i');
2219  }
2220  results = tocSearchInfo.filter( function (o) { return re.test(o.text); } );
2221
2222  if (results.length > 0) {
2223    // sort objects via name then namespace
2224    results = results.sort();
2225
2226    ul.className = LIST_ENUM[LIST_METHOD];
2227
2228    // Create RegEx (re) for search string highlight
2229    txt = txt.replace(/&/g, '&amp;');
2230    txt = txt.replace(/</g, '&lt;');
2231    txt = txt.replace(/>/g, '&gt;');
2232    if (isStart) {
2233      if (pre)
2234        re = new RegExp('^(' + pre + txt + ')', 'i');
2235      else
2236        re = new RegExp('^([#\.]?' + txt + ')', 'i');
2237    } else {
2238      re = new RegExp('(' + txt + ')', 'ig');
2239    }
2240  }
2241
2242  if (dbgSearchLoad) {
2243    tS = fmtTL(performance.now() - tSt, results.length);
2244    console.log('Search   exec    ' + tS + ' ' + text);
2245  }
2246
2247  oToc.items.style.display = 'none';
2248  ul.id = 'toc_search_items';
2249  oldUl = _id('toc_search_items');
2250  ul.appendChild( tocSearchCreate(results, re) );
2251  if (oldUl)
2252    oToc.nav.replaceChild(ul, oldUl);
2253  else
2254    oToc.nav.appendChild(ul);
2255//  listSearchExec2();
2256
2257//  t = null;
2258}
2259
2260/* Used for tocSearchInfo array.  Mostly so results sort can be done using
2261 * @param cls [String]
2262 */
2263function TocSObj(cls, href, idx, small, text) {
2264  this.cls   = cls;
2265  this.href  = href;
2266  this.idx   = idx;
2267  this.small = small;
2268  this.text  = text;
2269}
2270
2271TocSObj.prototype.toString = function TocSObjToString() {
2272//  return this.text.replace(re_meth_sep, '').toLowerCase() +
2273//    ("     " + this.idx).slice(-5);
2274  return this.text.replace(re_meth_sep, '').toLowerCase();
2275};
2276
2277/* Loads an array of TocSObj objects with method info.
2278 */
2279function tocSearchLoad(content) {
2280  var int_meths = content.querySelectorAll('ul.compact a'),
2281      ext_meths = content.querySelectorAll('a.i_m'),
2282      i, j, el, cls, small,
2283      reMethTrim = new RegExp( '(' + re_csep + '|' + re_isep + ')\\S+?$' );
2284
2285  tocSearchInfo = [];
2286  for (j = 0; el = int_meths[j]; j++) {
2287   tocSearchInfo.push( new TocSObj(el.className, el.href, i + j, '', el.textContent) );
2288  }
2289
2290  for (i = 0; el = ext_meths[i]; i++) {
2291    cls = el.className.replace(/private/, 'priv').replace(/protected/, 'prot');
2292
2293    new_id = decodeURIComponent(el.getAttribute('href'));
2294    new_id = new_id.replace(/(\.html)?#/, '_');
2295    new_id = new_id.replace(/^\.+\//, '');
2296    new_id = new_id.replace(/\//g, '_');
2297
2298    el.id = new_id;
2299
2300    cls = cls.replace(/i_m /, '').trim();
2301    small = el.title.match(/^\S+/)[0];
2302    if (small) small = small.replace(reMethTrim, '');
2303//    tocSearchInfo.push( new TocSObj(cls, el.href, i, small, el.textContent) );
2304    tocSearchInfo.push( new TocSObj(cls, '#' + new_id, i, small, el.textContent) );
2305  }
2306}
2307
2308//} @!endgroup
2309
2310//{ @!group Resizer Event Functions
2311
2312/* event MouseDown
2313 */
2314function resizeMD(e) {
2315  var stateWid;
2316
2317  e.preventDefault();
2318  e.stopPropagation();
2319
2320  if (e.which !== 1) {
2321    e.preventDefault();
2322    e.stopPropagation();
2323    return false;
2324  } else if (e.altKey || e.ctrlKey) {
2325    var elWid = this.e.offsetWidth,
2326        em = px30em/30.0,
2327        min, max;
2328    if (this === oList) {
2329      max = (LIST_MAX * em).toFixed(0); min = (LIST_MIN * em).toFixed(0);
2330    } else {
2331      max = ( TOC_MAX * em).toFixed(0); min = ( TOC_MIN * em).toFixed(0);
2332    }
2333
2334    if (winType === 1 && this.d.docked)
2335      stateWid = state.display[1].widthDocked;
2336    else
2337      stateWid = this.d.width;
2338
2339    if (e.altKey) {
2340      if (elWid == this.d.width) {
2341        this.s.width = (this.d.docked ? max : min) + 'px';
2342      } else {
2343        this.s.width = stateWid + 'px';
2344      }
2345    } else if (e.ctrlKey) {
2346      if (elWid == this.d.width) {
2347        this.s.width = (this.d.docked ? min : max) + 'px';
2348      } else {
2349        this.s.width = stateWid + 'px';
2350      }
2351    }
2352    return false;
2353  }
2354
2355  resizerStWid = this.e.offsetWidth;
2356  this.s.width = resizerStWid + 'px';
2357  resizerStX = e.screenX;
2358
2359  window.addEventListener('mousemove', this.resizeMM, false);
2360  window.addEventListener('mouseup'  , this.resizeMU, false);
2361  return false;
2362}
2363
2364/* MouseMove */
2365function resizeMM(e) {
2366    var offset   = e.screenX - resizerStX,
2367        newWidth = resizerStWid + (this.d.docked ? offset : -offset),
2368        em = px30em/ 30.0,
2369        minW = em * (this === oList ? LIST_MIN : TOC_MIN),
2370        maxW = em * (this === oList ? LIST_MAX : TOC_MAX);
2371
2372  newWidth = Math.max( Math.min(newWidth, maxW), minW);
2373  newWidth = newWidth.toFixed(0);
2374  this.s.width = newWidth + 'px';
2375  this.s.flexBasis = newWidth + 'px';
2376  if (winType === 1 && this.d.docked) cS.widthDocked = newWidth;
2377  else this.d.width = newWidth;
2378  // hdrDbg(newWidth + (this === oList ? '_List' : '_TOC') );
2379}
2380
2381/* MouseUp */
2382function resizeMU(e) {
2383  var newWidth = this.e.offsetWidth;
2384  this.s.width = newWidth + 'px';
2385  this.s.flexBasis = newWidth + 'px';
2386  if (winType === 1 && this.d.docked) cS.widthDocked = newWidth;
2387  else this.d.width = newWidth;
2388  window.removeEventListener('mousemove', this.resizeMM);
2389  window.removeEventListener('mouseup'  , this.resizeMU);
2390  if (storage) storage.yardState = JSON.stringify(state);
2391  updateCSS();
2392}
2393
2394/* TouchStart */
2395function resizeTS(e) {
2396  resizerStWid = this.e.offsetWidth;
2397  this.s.width = resizerStWid + 'px';
2398  resizerStX = e.touches[0].screenX;
2399  e.preventDefault();
2400  e.stopPropagation();
2401  window.addEventListener('touchmove', this.resizeTM, false);
2402  window.addEventListener('touchend' , this.resizeTE, false);
2403}
2404
2405/* TouchMove */
2406function resizeTM(e) { this.resizeMM( e.touches[0] ); }
2407
2408/* TouchEnd */
2409function resizeTE(e) {
2410  window.removeEventListener('touchmove', this.resizeTM);
2411  window.removeEventListener('touchend' , this.resizeTE);
2412  this.resizeMU(e);
2413}
2414
2415//} @!endgroup
2416
2417__loadPublicState();
2418__loadPublicState = undefined;
2419
2420})();
2421
2422//{ @!group Debug Functions
2423
2424/* Debug info about document size, element count, etc
2425 * @param content [HTMLDIVElement]
2426 */
2427function dbgDocInfo(content) {
2428  var lenTC = content.textContent.length / 1024.0,
2429      lenInnerHTML = content.innerHTML.length / 1024.0,
2430      lenChildren = content.querySelectorAll('*').length;
2431
2432  console.log( aDOMNext.href.match(/[^\/]+$/)[0].replace(/\.html/, '') +
2433    "\n  textContent   " + fmtL(lenTC) + ' KB' +
2434    "\n  innerHTML     " + fmtL(lenInnerHTML) + ' KB' +
2435    "\n  elements      " + fmtL(lenChildren)
2436  )
2437}
2438
2439/* Debug info about doc loading timing
2440 */
2441function dbgDocLoad() {
2442  var ttl = oDbgDoc.xhrRSC + oDbgDoc.cbAdd + oDbgDoc.cbToDOM + oDbgDoc.render,
2443      xhrRSC  = oDbgDoc.xhrRSC,
2444      cbAdd   = oDbgDoc.cbAdd,
2445      cbToDOM = oDbgDoc.cbToDOM,
2446      render  = oDbgDoc.render,
2447      xhrRcv  = oDbgDoc.xhrRcv,
2448      hgt = 'na',
2449      t;
2450
2451  if ( t = _id('footer') ) {
2452    hgt = t.offsetTop + t.offsetHeight;
2453    hgt = hgt / 1000.0;
2454  }
2455
2456  ttl = ('      ' + Math.round(ttl)).slice(-7);
2457
2458  console.log( aDOMCur.href.match(/[^\/]+$/)[0].replace(/\.html/, '') +
2459    "\n  DOC      TOTAL " + fmtT( ttl     ) +
2460    "\n  xhrRSC   parse " + fmtT( xhrRSC  ) +
2461    "\n  xhrCBDoc add   " + fmtT( cbAdd   ) +
2462    "\n  xhrCBDoc > DOM " + fmtT( cbToDOM ) +
2463    "\n  render         " + fmtT( render  ) +
2464    "\n  xhrRcv         " + fmtT( xhrRcv  ) +
2465    "\n  hgt pixels/1k "  + fmtL( hgt     )
2466  );
2467}
2468
2469/* Debug info about list loading timing
2470 */
2471function dbgListLoad() {
2472  var ttl      = oDbgList.xhrRSC + oDbgList.cbProc + oDbgList.render;
2473      xhrRSC   = oDbgList.xhrRSC,
2474      cbAdd    = oDbgList.cbProc,
2475      render   = oDbgList.render,
2476      xhrRcv   = oDbgList.xhrRcv,
2477      items    = oList.itemQty,
2478      itemsVis = oList.itemsVis,
2479      elements = oDbgList.elements,
2480      hgt = oList.items.getBoundingClientRect().height / 1000.0;
2481
2482  console.log( (oList.libRoot.replace(/\//g, '') + '               ').slice(0,17) +
2483    ' ' + _id('list_title').textContent +
2484    "\n  LIST   TOTAL   " + fmtT( ttl     ) +
2485    "\n  xhrRSC parse   " + fmtT( xhrRSC  ) +
2486    "\n  xhrCB  proc    " + fmtT( cbAdd   ) +
2487    "\n  render         " + fmtT( render  ) +
2488    "\n  xhrRcv         " + fmtT( xhrRcv  ) +
2489    "\n  hgt pixels/1k "  + fmtL( hgt     ) +
2490    "\n     ItemsVis   "  + fmtL( itemsVis) +
2491    "\n     Items      "  + fmtL( items   ) +
2492    "\n     Elements   "  + fmtL( elements)
2493  );
2494}
2495
2496/* Formats a time for console
2497 * @param t [Number]
2498 * @return  [String]
2499 */
2500function fmtT(t) {
2501  return ( '        ' + Math.round(t).toLocaleString() ).slice(-6) + ' ms';
2502}
2503
2504/* Formats an integer for console
2505 * @param l [Number]
2506 * @return  [String]
2507 */
2508 function fmtL(l) {
2509  return ('       ' + Math.round(l).toLocaleString()).slice(-7);
2510}
2511
2512function fmtTL(t, l) {
2513  return fmtT(t) + ' ' + fmtL(l);
2514}
2515
2516/* Debug to text area in main header */
2517function hdrDbg(str) {
2518  var el = _id('y_debug'),
2519      ary;
2520  if (!dbgYDebug || !el) return;
2521  ary = el.textContent.split(' ');
2522  ary[0] = str;
2523  el.textContent = ary.join(' ');
2524}
2525
2526//} @!endgroup
2527
2528//{ @!group Navigation & XHR Functions
2529
2530/* Called when new documents finish rendering
2531 */
2532function finishDocLoad() {
2533  setWait();
2534  if (oXHRDoc instanceof XMLHttpRequest) oXHRDoc = null;
2535  docReady();
2536  eContent.tabIndex = 1;
2537  eContent.focus();
2538  clickedBy  = -1;
2539  if (dbgDocTiming) {
2540    console.timeStamp('xhrCBDoc Render Done');
2541    oDbgDoc.render = performance.now() - oDbgDoc.render;
2542    dbgDocLoad();
2543  };
2544};
2545
2546/* Called anytime an anchor element is clicked, determines whether to call
2547 * xhrSend or gotoHash, loads url into aDOMNext
2548 * @param url [string] url to load
2549 */
2550function gotoDoc(url) {
2551  if (!isServer && url.slice(0,6) === '/file.') {
2552    aDOMNext.href = (oList.libRoot || '')  +  url.replace(/%-/, '%25-').slice(1);
2553  } else {
2554    aDOMNext.href = url.replace(/%-/, '%25-');
2555  }
2556
2557  if (aDOMCur.origin !== aDOMNext.origin) {
2558    window.location = url;
2559    return false;
2560  }
2561
2562  if (aDOMCur.pathname === aDOMNext.pathname && aDOMNext.hash) {
2563    gotoHash();
2564  } else {
2565    if (dbgDocTiming) console.timeStamp('xhrSend');
2566    if (oXHRDoc instanceof XMLHttpRequest) oXHRDoc.abort();
2567    setWait(CB_CONTENT);
2568    oXHRDoc = xhrSend(aDOMNext.href, xhrCBDoc);
2569  }
2570  return true;
2571}
2572
2573/* Called when hash navigation is required, uses aDOMNext for info.  Always
2574 * called by TOC list 'clicks', as TOC links are always for the document
2575 */
2576function gotoHash() {
2577  var hash = decodeURIComponent(aDOMNext.hash.replace(/%-/, '%25-')  ),
2578      title = winTitle + hash,
2579      el = _id(hash.slice(1)),
2580      prnt,
2581      nl,
2582      t,
2583      elOffTop;
2584
2585  if (!el) return;
2586
2587  if (el.tagName === 'SECTION' && el.firstElementChild.tagName === 'H3')
2588    el = el.firstElementChild;
2589  if ( /-constant$/.test(hash) && (t = _id('t2_cnst')) ) {
2590    if (t.classList.contains('h') ) t.classList.remove('h');
2591  }
2592
2593  if (eContentClicked) eContentClicked.classList.remove(CN_CLICKED);
2594
2595  // for constants
2596  if (el.tagName === 'DT') {
2597    prnt = el.parentElement;
2598    if (prnt.classList.contains('constants'))
2599      // prnt is DL, DL > div > h2
2600      prnt.parentElement.previousElementSibling.classList.remove(CN_HIDDEN);
2601  }
2602
2603  if (el instanceof HTMLAnchorElement && el.classList.contains('i_m') ) {
2604    nl = eContent.querySelectorAll('h2.h2_sum');
2605    for (var i = nl.length - 1, h2; h2 = nl[i]; i--) {
2606      if (el.compareDocumentPosition(h2) & 2) {
2607        h2.classList.remove(CN_HIDDEN);
2608        break;
2609      }
2610    }
2611  }
2612
2613  if (el) {
2614    eContentClicked = el;
2615    el.classList.add(CN_CLICKED);
2616    if (oList.d.vis) oList.showClicked(aDOMNext);
2617    if ( oToc.d.vis)  oToc.showClicked(aDOMNext);
2618  } else {
2619    el = eContent.querySelector('a[name="' + hash.slice(1) +'"]');
2620  }
2621
2622  if (el) {
2623    elOffTop = getOffsetTop(el);
2624    if (elOffTop === 0) {
2625      // constants have ids and maybe hidden by collapsed section
2626      if (el.parentElement.style.display === 'none') {
2627        el.parentElement.style.display = '';
2628        elOffTop = getOffsetTop(el);
2629      } else if (el.parentElement.parentElement.style.display === 'none') {
2630        el.parentElement.parentElement.style.display = '';
2631        elOffTop = getOffsetTop(el);
2632      }
2633    }
2634    if (clickedTop) {
2635      eContent.scrollTop = elOffTop - clickedTop;
2636    } else {
2637      el.scrollIntoView(true);
2638    }
2639//    eContent.focus();
2640    el.focus();
2641  }
2642
2643  if (aDOMCur.href === aDOMNext.href) return;
2644
2645  aDOMCur.href = aDOMNext.href;
2646  hash = aDOMCur.href;
2647  if (isWinHistory && (clickedBy !== CB_POP_STATE) ) {
2648    window.history.pushState({name: title, url: hash},
2649        title, hash.replace(/%-/, '%25-') );
2650  }
2651}
2652
2653/* xhr callback function when a new main document is ready, called by
2654 * xhrReadyStateChange
2655 * @param docFrag [DocumentFragment] body element of url document
2656 * @param title   [String] \<head\>\<title\> textContent
2657 * @param url     [String]
2658 * @param status  [Integer]
2659 * @param ms      [Float]   time to parse docFrag
2660 * @param msRcv   {Float]   time from xhrSend to xhrRSC
2661 */
2662function xhrCBDoc(docFrag, title, url, status, ms, msRcv) {
2663
2664  if (status !== 200 || docFrag === null) {
2665    setWait(-1);
2666    alert('xhrCBDoc - bad return with status ' + status + " from location\n" + url);
2667    if (clickedBy === CB_LIST) {
2668      oList.clickedLI = oList.clickedLIOld;
2669      oList.clickedA = null;
2670    }
2671    return;
2672  }
2673
2674  var hash = decodeURIComponent(aDOMNext.hash).slice(1),
2675      oldMenu = document.getElementById('y_menu'),
2676      oldContent = eContent,
2677      oldLibRoot,
2678      newContentClicked,
2679      newToc,
2680      newContent,
2681      newMenu,
2682      loadMenu = false,
2683      eAList,
2684      aDOMTemp = document.createElement('a'),
2685      tEl,
2686      t, t1, nl, tSt, tEnd;
2687
2688  if (dbgDocTiming) {
2689    console.timeStamp('xhrCBDoc Start');
2690    oDbgDoc.xhrRSC = ms;
2691    tSt = performance.now();
2692    oDbgDoc.xhrRcv = msRcv;
2693  }
2694
2695  // get content and objectPath from docFrag, along with list url
2696  if ( t = getDocFragId(docFrag, 'content') ) {
2697    if (hash) newContentClicked = getDocFragId(docFrag, hash);
2698    t1 = t.parentElement;
2699    newContent = t1.removeChild(t);
2700  };
2701
2702  // Can't load without both
2703  if (newContent === undefined || oldContent === undefined) return;
2704
2705  if ( t = getDocFragId(docFrag, 'y_header') ) {
2706    if (t1 = getDocFragId(docFrag, 'y_menu') )
2707      newMenu = t.removeChild(t1);
2708    if (t1 = getDocFragId(docFrag, 'list_href') )
2709      eAList  = t.removeChild(t1);
2710  };
2711
2712  docFrag = null;
2713
2714  if (newMenu) {
2715    isServer = (newMenu.className === 'server');
2716    if (oldMenu) {
2717      oldMenu.removeEventListener('click', clkMenu);
2718      loadMenu = true;
2719    };
2720  };
2721
2722  // Update history
2723  if (aDOMNext.href !== aDOMCur.href) {
2724    t = aDOMNext.pathname + (hash ? "#" + hash : '');
2725    if (isWinHistory && (clickedBy !== CB_POP_STATE) ) {
2726      window.history.pushState({name: title, url: t}, title, url);
2727    };
2728    aDOMCur.href = aDOMNext.href;
2729  };
2730
2731//console.log("eAList\n" + eAList.getAttribute('href') + "\nurl\n" + aDOMNext.href.replace(/[^\/]+$/, ''));
2732//console.log(aDOMNext.href.replace(/[^\/]+$/, '') + eAList.getAttribute('href'));
2733
2734  if (eAList) {
2735    eAList.href = aDOMNext.href.replace(/[^\/]+$/, '') + eAList.getAttribute('href');
2736    oList.load(eAList, false);
2737  }
2738
2739  if (dbgDoc) dbgDocInfo(newContent);
2740
2741  var eTitle  = document.documentElement.querySelector('head title'),
2742      eMain   = _id('y_main'),
2743      eHeader = _id('y_header');
2744
2745  isZoomed = false;
2746
2747  winTitle = title;
2748
2749  oldContent.removeEventListener('click', clkContent);
2750  eContentClicked = null;
2751  newToc = addContent(newContent);
2752
2753  if (!newContentClicked) {
2754    newContentClicked = newContent.querySelector('a[name="' + hash.slice(1) +'"]');
2755  } else if (newContentClicked.tagName === 'SECTION' && newContentClicked.firstElementChild.tagName === 'H3') {
2756    newContentClicked = newContentClicked.firstElementChild;
2757  }
2758
2759  if (newContentClicked) {
2760    newContentClicked.classList.add(CN_CLICKED);
2761    eContentClicked = newContentClicked;
2762  }
2763
2764  // below wraps tables in div element with overflow-x: auto
2765  t = Array.prototype.slice.call( newContent.querySelectorAll('table') );
2766  if (t.length > 0) {
2767    tEl = document.createElement('div');
2768    tEl.className = 'y_table';
2769    for (var i = 0, div, prnt, repl, tbl; tbl = t[i]; i++) {
2770      if (tbl.className === '') {
2771        div = tEl.cloneNode(false);
2772        prnt = tbl.parentElement;
2773        repl = prnt.replaceChild(div, tbl);
2774        div.appendChild(repl);
2775      }
2776    }
2777  }
2778
2779  if (dbgDocTiming) {
2780    console.timeStamp('xhrCBDoc Add Done');
2781    oDbgDoc.cbAdd = performance.now() - tSt;
2782    tSt = performance.now();
2783  }
2784
2785  // timeout to allow rendering
2786  setTimeout( finishDocLoad, 15);
2787
2788  // all the replacements at once
2789  eMain.replaceChild(newContent, oldContent);
2790  oToc.nav.replaceChild(newToc, oToc.items);
2791  if (t = _id('toc_search_items')) oToc.nav.removeChild(t);
2792
2793  if (loadMenu) eHeader.replaceChild(newMenu, oldMenu);
2794  eTitle.textContent = title;
2795
2796  // Shows or hides TOC search (none for doc content)
2797  if (!isFirstLoad) tocSearchShowHide(newContent);
2798
2799  // Scroll oList & oToc
2800  if (oList.d.vis) oList.showClicked(aDOMNext);
2801  oToc.items = _id('toc_items');
2802  if ( oToc.d.vis) {
2803    if (hash) oToc.showClicked(aDOMNext);
2804    else oToc.nav.scrollTop = 0;
2805  }
2806
2807  newContent = null;
2808  newToc = null;
2809  newMenu = null;
2810
2811  eContent = _id('content');
2812
2813  if (eContentClicked) {
2814    // open constants section if closed
2815    if ( /-constant$/.test(hash) && (t = _id('t2_cnst')) ) {
2816      if (t.classList.contains('h') ) t.classList.remove('h');
2817    }
2818    if (clickedTop) {
2819      eContent.scrollTop = eContentClicked.offsetTop - clickedTop;
2820    } else {
2821      eContentClicked.scrollIntoView(true);
2822    }
2823  }
2824
2825  if (dbgDocTiming) {
2826    console.timeStamp('xhrCBDoc ToDom Done');
2827    oDbgDoc.render  = performance.now();
2828    oDbgDoc.cbToDOM = oDbgDoc.render - tSt;
2829  }
2830}
2831
2832/* xhr onerror function
2833 * @param _url  [String]
2834 * @param _func [Function] The function to call when done processing
2835 * @param status [Integer]
2836 */
2837function xhrOnError(url, func, xhr) {
2838  var status = xhr.status;
2839  xhr.onreadystatechange = null;
2840  xhr.onerror = null;
2841  setWait(-1);
2842  func(null, null, url, status, 0, 0);
2843  return false;
2844}
2845
2846/* Receives xhr.responseXML, parses, loads into a DocumentFragment, which is
2847 *  returned to func callback.
2848 *
2849 * * Parses title element string
2850 * * Parses to just children of the body element
2851 * * Removes any script elements
2852 * @param url  [String]
2853 * @param func [Function] callback
2854 * @param xhr  [XMLHttpRequest]
2855 * @param pn   [Float] performance.now
2856 */
2857function xhrReadyStateChange(url, func, xhr, pn) {
2858 if (xhr.readyState === 4) {
2859    if (xhr.status === 200) {
2860      var title,
2861          docFrag = document.createDocumentFragment(),
2862          topDiv = document.createElement('div'),
2863          doc = xhr.responseXML,
2864          tRcv, tSt, tParse, nl, temp;
2865
2866      if (dbgDocTiming || dbgListTiming) {
2867        console.timeStamp('xhrRSC Start');
2868        tSt = performance.now();
2869        tRcv = tSt - pn;
2870      }
2871      if (doc === '') {
2872        func(null, null, url, 404, 0, tRcv);
2873        return;
2874      }
2875
2876      if (temp = doc.querySelector("head > title")) {
2877        title = temp.innerHTML;
2878        title = title.replace(/[\v\n\r]+/g, ' ');
2879        title = title.replace(/&nbsp;/g, ' ');
2880        title = title.replace(/ [^\w\d] /, ' - ').trim();
2881      } else title = '';
2882
2883      if (temp = doc.querySelector("head")) {
2884        // console.log("Deleting head");
2885        temp.parentElement.removeChild(temp);
2886      };
2887
2888      nl = doc.body.querySelectorAll('script');
2889      for (var k = 0, el; el = nl[k]; k++) {
2890        el.parentElement.removeChild(el);
2891      };
2892
2893      docFrag.appendChild(doc.body);
2894      // console.log(doc.body);
2895      doc = null;
2896      xhr.onreadystatechange = null;
2897      xhr.onerror = null;
2898
2899      if (dbgDocTiming || dbgListTiming)
2900        tParse = performance.now() - tSt;
2901
2902      func(docFrag, title, url, 200, tParse, tRcv);
2903      docFrag = null;
2904      xhr.onreadystatechange = null;
2905      xhr.onerror = null;
2906      xhr = null;
2907    } else if (xhr.status === 404) {
2908      // below is for RDoc links
2909      if (/classes\//.test(url) ) {
2910        url = url.replace(/classes\//, '');
2911        aDOMNext.href = aDOMNext.href.replace(/classes\//, '');
2912        xhr.open("GET", url , true);
2913        xhr.responseType = 'text';
2914        xhr.setRequestHeader('Content-Type', 'text/html');
2915        xhr.send();
2916        return false;
2917      }
2918      xhr.onreadystatechange = null;
2919      xhr.onerror = null;
2920      func(null, null, url, 404, tParse, tRcv);
2921    }
2922  }
2923}
2924
2925/* Preps, opens, and sends the xhr request
2926 * @param url  [String]
2927 * @param func [Function] The function to call when done processing
2928 * @return [XMLHttpRequest] xhr object, used for abort
2929 */
2930function xhrSend(url, func) {
2931  var aDOM = document.createElement('a'),
2932      xhr = new XMLHttpRequest(),
2933      tSt,
2934      reFullHost = /^[^:]+:\/\/[^\/]+/;
2935
2936  aDOM.setAttribute('href', url);
2937
2938  if (reFullHost.exec(aDOM.href)[0] !== reFullHost.exec(location.href)[0]) {
2939    func(null, url);
2940    return;
2941  };
2942  tSt = performance.now();
2943  xhr.onreadystatechange = xhrReadyStateChange.bind({}, url, func, xhr, tSt);
2944  xhr.onerror = xhrOnError.bind({}, url, func, xhr);
2945
2946  xhr.open("GET", url , true);
2947  xhr.responseType = 'document';
2948  xhr.setRequestHeader('Content-Type', 'text/html');
2949  xhr.send();
2950  return xhr;
2951}
2952
2953//} @!endgroup
2954
2955//{ @!group Misc & Helper Functions
2956
2957/* Convenience method for document.getElementById()
2958 *  @param id  [String]
2959 *  @return    [HTMLElement]
2960 */
2961function _id(id) {
2962  //if (id === '' || !id) debugger;
2963  return document.getElementById(id);
2964}
2965
2966/* Changes font size in state, then calls {setFontSize}.
2967 * @param [Boolean] up up for increased fontSize, otherwise decrease.
2968 */
2969function changeFontSize(up) {
2970  var fs = parseInt(state.fontSize.replace('px', '') || '15px') + (up ? 1 : -1);
2971  if      (fs <= 12)  fs = 12;
2972  else if (fs >= 18)  fs = 18;
2973  state.fontSize = fs + 'px';
2974  if (storage) storage.yardState = JSON.stringify(state);
2975  setFontSize();
2976  if (_id('settings').className === 'show') settingsSetData();
2977};
2978
2979/* Cleans white space nodes from prnt.
2980 * @param [HTMLElement] prnt
2981 */
2982function clean(prnt) {
2983  for (var i = 0, el; el = prnt.childNodes[i]; i++) {
2984    if ( (el.nodeType === Node.TEXT_NODE && !/\S/.test(el.nodeValue)) ||
2985          el.nodeType === Node.COMMENT_NODE) {
2986      prnt.removeChild(el);
2987      i --;
2988    }
2989    else if (el.nodeType === Node.ELEMENT_NODE) clean(el);
2990  };
2991};
2992
2993function commonLoad() {
2994  var foundHRef;
2995  for (var i = 0, ss; ss = document.styleSheets[i]; i++) {
2996    if ( /y_style\.css$/.test(ss.href) ) {
2997      foundHRef = ss.href.replace(/css\/y_style.css$/, 'xhr/common.html');
2998      xhrSend(foundHRef, commonXhrCB);
2999      break;
3000    };
3001  };
3002};
3003
3004function commonInsert(docFrag, elId) {
3005  var newPrnt = _id(elId),
3006      oldPrnt = isGetElByIdInDocFrag ? docFrag.getElementById(elId) : docFrag.querySelector("#" + elId),
3007      el;
3008  clean(oldPrnt);
3009  while (el = oldPrnt.firstElementChild) { newPrnt.appendChild(el); };
3010};
3011
3012function commonXhrCB(docFrag, title, url, status, ms, msRcv) {
3013  var eAList = _id('list_href').cloneNode(false),
3014      el;
3015  if (status !== 200 || !docFrag) {
3016    setWait();
3017    return;
3018  };
3019
3020  if (_id('y_header').children.length === 5) {
3021    commonInsert(docFrag, 'y_header');
3022//    setFSTitles();
3023  };
3024  if (!_id('list_header').firstElementChild) {
3025    commonInsert(docFrag, 'list_header');
3026  };
3027  if (!_id('toc_header').firstElementChild) {
3028    commonInsert(docFrag, 'toc_header');
3029  };
3030  if (!_id('settings').firstElementChild) {
3031    commonInsert(docFrag, 'settings');
3032  };
3033
3034  // Add events to items added above
3035  oPane.firstDOMLoad(true);
3036  _id('hdr_button').addEventListener('click'        , clkHdrBtn         , false);
3037  _id('settings'  ).addEventListener('click'        , clkSettingWindow  , false);
3038
3039  // Load list
3040  if (eAList) oList.load(eAList);
3041  // Set LIST and TOC button state in main header
3042  oList.bVis = _id('list_vis');
3043  oList.bVis.className = oList.d.vis ? CN_BTN_VIS : CN_HIDDEN;
3044  oToc.bVis  = _id('toc_vis');
3045  oToc.bVis.className = oToc.d.vis ? CN_BTN_VIS : CN_HIDDEN;
3046  // Set source button state
3047  _id('src').className = state.showSource ? 'o' : 'c';
3048  _id('src').disabled = !eContent.querySelector('a.toggleSource')
3049  // Shows or hides TOC search (none for doc content)
3050  tocSearchShowHide(eContent);
3051};
3052
3053/* To match Ruby CGI.escape /([^ a-zA-Z0-9_.-]+)/
3054 * @param str [String] unescaped string
3055 * @return [String] escaped string
3056 */
3057function encodeStr(str) {
3058  return str.replace(/[^ a-zA-Z0-9_.-]/g, encodeChar );
3059}
3060
3061/* Escape string as valid regex
3062 * @param str [String] unescaped string
3063 * @return [String] escaped string
3064 */
3065function escapeRegEx(str) {
3066  return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
3067}
3068
3069/* IE fun...
3070 *
3071 */
3072function getDocFragId(frag, id) {
3073  if (isGetElByIdInDocFrag)
3074    return frag.getElementById(id);
3075  else
3076    var escId = id.replace(/[`<>&\[\]/{}()*+?!.\^$|=@-]/g, "\\$&")
3077    // console.log(escId);
3078    return frag.querySelector('#' + escId);
3079};
3080
3081function tocSearchShowHide(content) {
3082  cls = content.className;
3083  if (cls === 'module' || cls === 'class' || cls === 'method') {
3084    _id('toc_search_text').style.display = '';
3085    _id('toc_search_text').value = '';
3086    _id('toc_search_icon').style.display = '';
3087
3088  } else {
3089    _id('toc_search_text').style.display = 'none';
3090    _id('toc_search_icon').style.display = 'none';
3091  }
3092}
3093
3094/* Used to match encoding of method list, two % replace statements for FireFox's
3095 * handling of '<' and '>'
3096 * @param str [String] unescaped string
3097 * @return [String] escaped string
3098 */
3099function t2EncodeStr(str) {
3100  //str = str.replace(/[\/\.?+%]/g, encodeChar);
3101  // encode + 2B   / 2F   ? 3F
3102  //  str = str.replace(/[\/\.?+]/g, encodeChar);
3103
3104  str = str.replace(/[`\/?+%^]/g, encodeChar);
3105  str = str.replace(/%3C/g, '\<' );
3106  str = str.replace(/%3E/g, '\>' );
3107//str = str.replace(/%3F/g, '\?>' );
3108  return str;
3109}
3110
3111/* Return escaped char
3112 * @param c [Char] single character string
3113 * @return [String] escaped string, ie '!' => '%21'
3114 */
3115function encodeChar(c) {
3116  return '%' + c.charCodeAt(0).toString(16).toUpperCase();
3117}
3118
3119/* Finds a css rule.
3120 * @param [String] file base file name of stylesheet
3121 * @param {String] rule selectorText of rule
3122 */
3123function getCSSRule(file, rule) {
3124  var foundRule,
3125      re = new RegExp( escapeRegEx(file) );
3126  for (var i = 0, ss; ss = document.styleSheets[i]; i++) {
3127//    if ( ss.href.endsWith(file) ) {
3128
3129    if ( re.test(ss.href) ) {
3130      cssRules = ss.cssRules;
3131      for (var j = 0, cssRule; cssRule = cssRules[j]; j++) {
3132        // style
3133        if (cssRule.selectorText === rule) {
3134          foundRule = cssRule;
3135          break;
3136        };
3137      };
3138      break;
3139    };
3140  };
3141  return foundRule;
3142};
3143
3144/* Finds the first parent element whose class matches using instanceof.
3145 * @param el [HTMLElement]
3146 * @param io [class HTMLElement] instanceof comparision class
3147 * @param notCurrent [Boolean] optional, whether to not test el parameter,
3148 *   if not specified, function will test el
3149 * @return [HTMLElement] the parent element matching io
3150 */
3151function getPrntByIO(el, io, notCurrent) {
3152  if (!notCurrent && el instanceof io) return el;
3153  var prnt = el.parentElement;
3154  while ( prnt && !(prnt instanceof io)) { prnt = prnt.parentElement; }
3155  return prnt;
3156}
3157
3158/* returns offsetTop of element
3159 * @param el [HTMLElement]
3160 * @return [Integer] total offsetTop
3161 */
3162function getOffsetTop(el) {
3163  var y = el.offsetTop;
3164  while (el = el.offsetParent) { y += el.offsetTop; }
3165  return y;
3166}
3167
3168/* Shows 'wait' svg
3169 * @param type [Integer, null] CB_CONTENT or CB_LIST, null to clear
3170 */
3171function setWait(type) {
3172  var oBCR,
3173      oBCRW,
3174      itemsX,
3175      eWait = _id('y_wait'),
3176      cls = isTouch ? 'text' : 'run',
3177      halfWid = 0.5 * waitWidth,
3178      t;
3179
3180  if (type === CB_CONTENT) {
3181    oBCR = eContent.getBoundingClientRect();
3182    eWait.style.top  = Math.round(oBCR.top  + halfWid) + 'px';
3183    eWait.style.left = Math.round(oBCR.left  + oBCR.width/2.0 - halfWid) + 'px';
3184    eWait.setAttribute('class', cls);
3185  } else if (type === CB_LIST) {
3186    // oList.items maybe hidden due to search
3187    if (t = _id('list_search_items') ) {
3188      oBCRW = t.getBoundingClientRect();
3189      itemsX = oBCRW.left + oBCRW.width/2.0;
3190    }
3191    if ( !(itemsX > 15 )) {
3192      if (oList.items) {
3193        oBCRW = oList.items.getBoundingClientRect();
3194        itemsX = oBCRW.left + oBCRW.width/2.0;
3195      }
3196    }
3197    if ( !(itemsX > 15 )) {
3198      oBCR = oList.nav.getBoundingClientRect();
3199      itemsX = oBCR.left + oBCR.width/2.0;
3200    }
3201    oBCR = _id('list_header').getBoundingClientRect();
3202    eWait.style.top  = Math.round(oBCR.top + oBCR.height + halfWid) + 'px';
3203    eWait.style.left = Math.round(itemsX   - halfWid) + 'px';
3204    eWait.setAttribute('class', cls);
3205  } else
3206    eWait.setAttribute('class', '');
3207}
3208
3209/* Globally shows / hides source code
3210 * @param content  [HTMLElement] the content element
3211 * @param showCode [Boolean]     true to show source
3212 */
3213function sourceShowHide(content, showCode) {
3214  var src =  content.querySelectorAll('section div.source_code'),
3215      sToggle = content.querySelectorAll('span.showSource a.toggleSource'),
3216      len,
3217      top = 0.0,
3218      eShow,
3219      hdrBottom = _id('y_header').getBoundingClientRect();
3220
3221  hdrBottom = hdrBottom.top + hdrBottom.height;
3222
3223  if (eContentClicked) {
3224    top = Math.round(eContentClicked.getBoundingClientRect().top);
3225  }
3226  if (hdrBottom < top && top < winHeight) {
3227    eShow = eContentClicked;
3228  } else {
3229    var nl = eContent.querySelectorAll('h2, h3');
3230    // eContentClicked not visible
3231    for (var i = 0, el, topTest; el = nl[i]; i++) {
3232      topTest = el.getBoundingClientRect().top;
3233      if (hdrBottom < topTest && topTest < winHeight) {
3234        top = Math.round(topTest);
3235        eShow = el;
3236        break;
3237      }
3238    }
3239  }
3240
3241  len = src.length - 1;
3242
3243  if (showCode) {
3244    _id('src').className = 'o';
3245    for (var i = len; i >= 0; i--) {
3246      src[i].classList.remove(CN_HIDDEN);
3247      sToggle[i].textContent = sToggle[i].textContent.replace(/^view/, 'hide');
3248    }
3249  } else {
3250    _id('src').className = 'c';
3251    for (var i = len; i >= 0; i--) {
3252      src[i].classList.add(CN_HIDDEN);
3253      sToggle[i].textContent = sToggle[i].textContent.replace(/^hide/, 'view');
3254    }
3255  }
3256
3257  if (top !== 0.0) {
3258    top = eContent.scrollTop + Math.round(eShow.getBoundingClientRect().top) - top;
3259    eContent.scrollTop = top;
3260  }
3261}
3262
3263/* Called when Summary h2 elements are clicked
3264 * @param tgt [HTMLAnchorElement]
3265 */
3266function summaryToggle(tgt) {
3267  var nlToggles = eContent.querySelectorAll('h2.h2_sum'),
3268      rTop = tgt.offsetTop - eContent.scrollTop,
3269      isHidden = tgt.classList.contains(CN_HIDDEN),
3270      a, h2El;
3271
3272  nlToggles = Array.prototype.slice.call(nlToggles);
3273
3274//  if (nlToggles.length > 0) {
3275    // code below for js constants & variables, assumes they're
3276    // singular and at the top of the nodelist
3277//    h2El = nlToggles[0];
3278//   if (h2El.id === 'js_h2_cv') {
3279//      a = nlToggles.shift();
3280//      a.textContent = text;
3281//      grandParent.nextElementSibling.style.display = (text === 'collapse') ? '' : 'none';
3282//    }
3283//  }
3284
3285  len = nlToggles.length - 1;
3286  if (isHidden) {
3287    for (var i = len; i >= 0; i--) { nlToggles[i].classList.remove(CN_HIDDEN); }
3288  } else {
3289    for (var i = len; i >= 0; i--) { nlToggles[i].classList.add(CN_HIDDEN); }
3290  }
3291  eContent.scrollTop = tgt.offsetTop - rTop;
3292  state.summaryCollapsed = !isHidden;
3293  if (storage) storage.yardState = JSON.stringify(state);
3294}
3295
3296/* Sets width and height of Settings transition style.
3297 * @param [Integer] wid width of Settings div
3298 * @param [Integer] hgt height of Settings div
3299 */
3300function settingsSetCSS(type) {
3301  var el   = _id('settings');
3302
3303  if (type === 'in')          el.className = 'show';
3304  else if (type === 'out')  el.className = '';
3305  else alert("Parameter Error setSettingsCSS!");
3306};
3307
3308/* Sets settings 'window' items */
3309function settingsSetData() {
3310  var scrnChar = Math.floor(50.0 * screen.availWidth / px50Char),
3311      scrnType = scrnChar > MED_LRG ? 2 : (scrnChar > SML_MED ? 1 : 0),
3312      fs = state.fontSize;
3313  settingsSetTitles(fs);
3314  _id('b2').textContent = winType;
3315  _id('c2').textContent = scrnType;
3316  _id('b3').textContent = Math.floor(50.0 * winWidth / px50Char);
3317  _id('c3').textContent = scrnChar;
3318  _id('a5').textContent = 'Font ' + fs;
3319
3320  if (fs === '12px') changeClass( _id('font_sml'), CN_BTN_VIS, null      , true, true);
3321  else               changeClass( _id('font_sml'), null      , CN_BTN_VIS, true, false);
3322
3323  if (fs === '18px') changeClass( _id('font_lrg'), CN_BTN_VIS, null      , true, true);
3324  else               changeClass( _id('font_lrg'), null      , CN_BTN_VIS, true, false);
3325
3326  if (winType === 0) {
3327    changeClass( _id('list_dock'  ), CN_BTN_VIS, null      , null, true);
3328    changeClass( _id('toc_dock'   ), CN_BTN_VIS, null      , null, true);
3329    changeClass( _id('list_float' ), null      , 'selected', null, true);
3330    changeClass( _id('toc_float'  ), null      , 'selected', null, true);
3331  } else {
3332    changeClass( _id('list_dock' ), null, 'selected',  oList.d.docked, null);
3333    changeClass( _id('list_float'), null, 'selected', !oList.d.docked, null);
3334    changeClass( _id('toc_dock'  ), null, 'selected',   oToc.d.docked, null);
3335    changeClass( _id('toc_float' ), null, 'selected',  !oToc.d.docked, null);
3336  };
3337};
3338
3339/* Sets title of font size buttons */
3340function settingsSetTitles(_fs) {
3341  var fs = parseInt( (_fs || state.fontSize).replace('px', '') );
3342  if (fs <= 12) {
3343    _id('font_sml').title = '12px'         ; _id('font_lrg').title = '13px';
3344  } else if (fs >= 18) {
3345    _id('font_sml').title = '17px'         ; _id('font_lrg').title = '18px';
3346  } else {
3347    _id('font_sml').title = (fs - 1) + 'px'; _id('font_lrg').title = (fs + 1) + 'px';
3348  };
3349};
3350
3351/* Used to check whether settings 'window' is open and shoud be closed.  Called
3352 * from various event handlers.
3353 */
3354function settingsCheck() {
3355  var el = _id('settings');
3356  if ( el && el.className === 'show') settingsSetCSS('out');
3357}
3358
3359/* Adds / removes classes from an element, also can disable.
3360 * @param [HTMLElement]   el      id of element
3361 * @param [String, null]  rmvCls  class to remove
3362 * @param [String, null]  addCls  class to add
3363 * @param [Integer, null] blnAdd  null no action, false remove addCls,
3364 *    true adds addCls
3365 * @param [Integer, null] blnDis  disable flag if not null
3366 */
3367function changeClass(el, rmvCls, addCls, blnAdd, blnDis) {
3368  var cl = el.classList;
3369  if (rmvCls) cl.remove(rmvCls);
3370  if (addCls) {
3371    if      (blnAdd === true)  cl.add(addCls);
3372    else if (blnAdd === false) cl.remove(addCls);
3373  };
3374  if (blnDis !== null) el.disabled = blnDis;
3375}
3376
3377//} @!endgroup
3378
3379//{ @!group Add Doc Content Functions
3380
3381/* Main function that calls the remainder of the 'add' functions
3382 * @param content [HTMLDIVElement] the content div element
3383 */
3384function addContent(content) {
3385  var cls = content.className,
3386      toc, t, footer;
3387
3388  if (t2GA instanceof Function) t2GA('send', 'pageview', aDOMNext.pathname);
3389
3390  // for javascript files
3391  if ( typeof hljs !== 'undefined' &&
3392      hljs.highlightElement instanceof Function ) {
3393//    var nl = content.querySelectorAll('pre.code.javascript, pre.code.cpp');
3394    var nl = content.querySelectorAll('pre.code');
3395    for (var i = 0, el; el = nl[i]; i++) {
3396      if ( !(el.classList.contains('ruby') || el.classList.contains('example') ) )
3397        hljs.highlightElement(el);
3398    }
3399  }
3400
3401  if (cls === 'module' || cls === 'class' || cls === 'method') {
3402    removeEmptyDocString(content);
3403
3404    t = content.querySelector('#h2_overview');
3405    if (state.overviewCollapsed && t) t.classList.add(CN_HIDDEN);
3406
3407    t = content.querySelector('#t2_relations');
3408    if (state.relationsCollapsed && t) {
3409      t.classList.add(CN_HIDDEN);
3410      // td > tr > thead > table
3411      var tbody = t.parentElement.parentElement.parentElement.querySelector('tbody');
3412      tbody.style.display = 'none';
3413    }
3414
3415    addWhiteSpace(content);
3416    addLineNumbers(content);
3417    addShowSource(content);
3418    addSummaryCompact(content);
3419    toc = oToc.generate(content, true);
3420    if (isServer && cls !== 'method') addPermaLinks(content);
3421    t = content.querySelector('a.toggleSource');
3422  } else {
3423    toc = oToc.generate(content, false);
3424    t = false;
3425  }
3426  if ( _id('src') ) _id('src').disabled = !t;   // Source open/close button
3427
3428  addFooter(content);
3429
3430  return toc;
3431}
3432
3433/* Adds footer children
3434 * @param content [HTMLDIVElement] the content div element
3435 */
3436function addFooter(content) {
3437  var footer = content.querySelector('#footer'),
3438      el;
3439
3440  if (footer && footer.children.length === 0 &&
3441      elLibFooter && elLibFooter.children.length > 0) {
3442    while (el = footer.firstElementChild) { footer.removeChild(el); };
3443    for (var i = 0, node; node= elLibFooter.childNodes[i]; i++) {
3444      footer.appendChild(node.cloneNode(true));
3445    };
3446    // console.log("elLibFooter " + elLibFooter.textContent.length + "  footer " + footer.textContent.length);
3447  };
3448};
3449
3450/* Adds Line number text to code segments
3451 * @param content [HTMLDIVElement] the content div element
3452 */
3453function addLineNumbers(content) {
3454  var nl = content.querySelectorAll('div.source_code pre.lines_num'),
3455      numStart,
3456      numEnd,
3457      str;
3458
3459  for (var i = 0, el; el = nl[i]; i++) {
3460    if (el.textContent == '') {
3461      numStart = Number(el.dataset.start);
3462      numEnd = Number(el.dataset.end);
3463      if (numStart > 0) {
3464        str = "\n\n";
3465        for (var j = numStart; j <= numEnd; j++) {
3466          str += j + "\n";
3467        }
3468        el.textContent = str;
3469      }
3470    }
3471  }
3472}
3473
3474/* If running server, adds the 'permalink' to method signatures in
3475 *  detail sections
3476 * @param content [HTMLDIVElement] the content div element
3477 */
3478function addPermaLinks(content) {
3479  var nl = content.querySelectorAll('section h3.signature'),
3480      a = document.createElement('a'),
3481      docPath = aDOMNext.pathname;
3482
3483  a.className   = 'permalink';
3484  a.textContent = 'permalink';
3485
3486  for (var i = 0, el, aa, href; el = nl[i]; i++) {
3487    aa = a.cloneNode(true);
3488    href = el.parentElement.id;
3489    if ( /-class_method$/.test(href) )
3490      href = '.' + href.replace(/-class_method$/, '');
3491    else
3492      href = ':' + href.replace(/-[a-z_]+$/, '');
3493    href = href.replace(/\?$/, '%3F');
3494    aa.setAttribute('href', docPath + href);
3495//      el.appendChild(aa);
3496    el.insertBefore(aa, el.firstChild);
3497  }
3498}
3499
3500/* Adds the 'view / hide source' elements for source code
3501 * @param content [HTMLDIVElement] the content div element
3502 */
3503function addShowSource(content) {
3504  var nl = content.querySelectorAll('section.method_details div.source_code'),
3505      span = document.createElement('span'),
3506      spc = String.fromCharCode(8202),
3507      showSource = state.showSource;
3508
3509  span.className = 'showSource';
3510  span.innerHTML = "[" + spc + "<a href='#' class='toggleSource'>" +
3511    (showSource ? T_HIDE_SRC : T_VIEW_SRC) + "</a>" + spc + "]";
3512
3513  if ( _id('src') ) _id('src').disabled = (nl.length === 0);
3514
3515  for (var i = 0, el; el = nl[i]; i++) {
3516    if (showSource) el.classList.remove(CN_HIDDEN);
3517    else            el.classList.add(CN_HIDDEN);
3518    if (el.previousElementSibling.className === 'link_repo')
3519      el = el.previousElementSibling;
3520    el.parentElement.insertBefore( span.cloneNode(true) , el);
3521  }
3522}
3523
3524/* Add elements for compact summaries
3525 * @param content [HTMLDIVElement] the content div element
3526 */
3527function addSummaryCompact(content) {
3528//var nl = content.querySelectorAll('ul.summary.full'),
3529  var nl = content.querySelectorAll('ul.summary'),
3530      ulFull,
3531      ulCompact,
3532      isConstant,
3533      remove,
3534      nlChildren,
3535      prnt,
3536      grandParent,
3537      fullVis = !state.summaryCollapsed,
3538      compactVis = state.summaryCollapsed,
3539      eANew,
3540      eId,
3541      eA  = document.createElement('a'),
3542      eUL = document.createElement('ul'),
3543      eLI = document.createElement('li'),
3544      t;
3545
3546  // Loop thru all the 'summary full' ul elements, clone for compact summaries,
3547  // then remove content
3548  for (var i = 0; ulFull = nl[i]; i++) {
3549    ulCompact = eUL.cloneNode(false);
3550    ulFull.classList.add('full');
3551    ulCompact.className = ulFull.className.replace(' full', ' compact');
3552    isConstant = ulFull.classList.contains('constants');
3553    // get anchor elements, clean up, append to ulCompact
3554    if (isConstant)
3555      nlChildren = ulFull.querySelectorAll('span.summary_signature');
3556    else
3557      nlChildren = ulFull.querySelectorAll('span.summary_signature a');
3558
3559    for (var j = 0, a, li, cls; a = nlChildren[j]; j++) {
3560      if (isConstant) {
3561        cls = a.className.replace(/^summary_signature/, 'cnst_a').trim();
3562        eANew = eA.cloneNode();
3563        eANew.href = '#' + a.id;
3564        eANew.textContent = a.textContent.replace(/ =$/, '');
3565      } else {
3566        cls = a.parentElement.className.replace('summary_signature', '').trim();
3567        if (a.parentElement.parentElement.classList.contains('deprecated'))
3568          cls += ' deprecated';
3569        eANew = a.cloneNode(true);
3570        // remove text in anchor but only after strong
3571        for (var k = 0, child, del = false; child = eANew.childNodes[k]; k++) {
3572          if (del) {
3573            eANew.removeChild(child);
3574            k--;
3575          } else {
3576            del = (child.nodeType === 1 && child.tagName === 'STRONG');
3577          }
3578        }
3579        // remove anything after anchor
3580        while (t = eANew.nextSibling) {
3581          eANew.parentElement.removeChild(t);
3582        }
3583      }
3584
3585      if (cls !== '') eANew.className = cls;
3586
3587      li = eLI.cloneNode(false);
3588      li.appendChild(eANew);
3589      ulCompact.appendChild(li);
3590    }
3591    ulFull.parentElement.insertBefore(ulCompact, ulFull);
3592  }
3593
3594  if (compactVis) {
3595    nl = content.querySelectorAll('h2.h2_sum');
3596    for (var i = 0, el; el = nl[i]; i++) { el.classList.add(CN_HIDDEN); }
3597  }
3598}
3599
3600/* Adds small unicode space characters to long document titles, long class names
3601 * in particular.  This allows the text to wrap.  Without this, the long names
3602 * would force the document window width larger than the UI accomodates.
3603 * @param content [HTMLDIVElement] the content div element
3604 */
3605function addWhiteSpace(content) {
3606  var title = content.querySelector('h1'),
3607      spc = String.fromCharCode(8202);
3608
3609  if (title && title.textContent.length > 40) {
3610    for (var i = 0, node; node = title.childNodes[i]; i++) {
3611      if (node.nodeType === 3) {
3612        node.nodeValue = node.nodeValue.replace(/::/g, '::' + spc);
3613        node.nodeValue = node.nodeValue.replace(/([a-z])([A-Z])/g, "\$1" + spc + "\$2");
3614      }
3615    }
3616  }
3617}
3618
3619/* removes empty docstring divs
3620 * @param content [HTMLDIVElement] the content div element
3621 */
3622function removeEmptyDocString(content) {
3623  var nl = content.querySelectorAll('div.docstring, div.tags'),
3624      prnt;
3625  for (var i = 0, el; el = nl[i]; i++) {
3626    if (el.textContent.trim() === '') {
3627      prnt = el.parentElement;
3628      prnt.removeChild(el);
3629    }
3630  }
3631  nl = null;
3632}
3633
3634//} @!endgroup
3635
3636//{ @!group Window & Main Doc Event Handlers
3637
3638/* Event handler for main header button click.  At present, the only active
3639 * controls are the four Pane control button, which are called via oPane.mainClk.
3640 */
3641function clkHdrBtn(e) {
3642  var tgt = e.target,
3643      tag = tgt.tagName,
3644      cls = tgt.className,
3645      id = tgt.id;
3646
3647  if (tag === 'CODE'  || tag === 'svg' || tag === 'SPAN') {
3648    // move up to button (code element used for the triangles)
3649    tgt = tgt.parentElement;
3650    tag = tgt.tagName;
3651    cls = tgt.className;
3652    id  = tgt.id;
3653  } else if (tag === 'rect' || tag === 'path') {
3654    tgt = tgt.parentElement.parentElement;
3655    tag = tgt.tagName;
3656    cls = tgt.className;
3657    id  = tgt.id;
3658  }
3659
3660  if (tgt instanceof HTMLButtonElement) {
3661    if (tgt.disabled) return;
3662    switch (id) {
3663      case 'src':
3664        settingsCheck();
3665        state.showSource = (_id('src').className === 'c');
3666        if (storage) storage.yardState = JSON.stringify(state);
3667        sourceShowHide(eContent, state.showSource);
3668        break;
3669      case 'sh_settings':
3670        el = _id('settings');
3671        el.style.willChange = 'opacity, max-height, max-width';
3672        settingsSetData();
3673//        if (el.className === 'hidden') {
3674        if (el.className !== 'show') {
3675//          el.className = '';
3676          settingsSetCSS('in');
3677        } else {
3678          settingsSetCSS('out');
3679        };
3680        break;
3681      default:
3682        settingsCheck();
3683        if ( HDR_PANE_BTNS.indexOf(id) > -1 )
3684          oPane.mainClk(id);
3685        break;
3686    };
3687  } else settingsCheck();
3688};
3689
3690/* Event handler for main header Namespace/Object path click.  Locates the URL,
3691 * then calls gotoDoc.
3692 */
3693function clkMenu(e) {
3694  var tgt = e.target,
3695      url,
3696      cancel = false;
3697
3698  settingsCheck();
3699
3700  if (tgt instanceof HTMLAnchorElement) {
3701    if (tgt.id === 't2_doc_top') {
3702      eContent.scrollTop = 0;
3703      cancel = true
3704    } else if (!e.ctrlKey) {
3705      if (tgt.id === 'home_no_xhr') { return true }
3706      url = tgt.href;
3707      if (url) {
3708        clickedBy = CB_OBJ_PATH;
3709        if ( /github\.io/i.test(url) && /\/_index\.html$/.test(url) )
3710          url = url.replace(/\/_index\.html$/, '/y_index.html');
3711        gotoDoc(url);
3712        cancel = true;
3713      }
3714    }
3715  }
3716  if (cancel) {
3717    e.stopPropagation();
3718    e.preventDefault();
3719    return false;
3720  }
3721}
3722
3723/* Event handler for content click.  Manages the content UI. */
3724function clkContent(e) {
3725  var tgt = e.target,
3726      tag = tgt.tagName,
3727      cls = tgt.className,
3728      clsList,
3729      id = tgt.id,
3730      eA,
3731      hash,
3732      prnt,
3733      len,
3734      cancel = false,
3735      reCls = /(toggleSource|summary_signature|h2_sum|h2_sum nodoc|cnst_a)/,
3736      reIds = /(h2_overview|t2_defined_in|t2_relations|t2_inherits)/,
3737      attrHRef = tgt.getAttribute('href');
3738
3739  settingsCheck();
3740
3741  // standard internal link
3742  if (attrHRef && /^#\S+/.test(attrHRef)) { return true };
3743
3744  if (reIds.test(id)) {
3745    var t;
3746    clsList = tgt.classList;
3747    switch (id) {
3748    case 'h2_overview':
3749      state.overviewCollapsed = tgt.classList.toggle(CN_HIDDEN);
3750      if (storage) storage.yardState = JSON.stringify(state);
3751      cancel = true;
3752      break;
3753    case 't2_defined_in':
3754      t = getPrntByIO(tgt, HTMLTableRowElement).querySelector("span.defines")
3755      if (clsList.contains(CN_HIDDEN)) {
3756        clsList.remove(CN_HIDDEN);
3757        clsList.add(CN_VISIBLE);
3758        if (t) t.style.display = 'none';
3759      } else if (clsList.contains(CN_VISIBLE)) {
3760        clsList.remove(CN_VISIBLE);
3761        clsList.add(CN_HIDDEN);
3762        if (t) t.style.display = 'inline';
3763      }
3764      cancel = true;
3765      break;
3766    case 't2_inherits':
3767      if (clsList.contains(CN_HIDDEN)) {
3768        clsList.remove(CN_HIDDEN);
3769        clsList.add(CN_VISIBLE);
3770      } else if (clsList.contains(CN_VISIBLE)) {
3771        clsList.remove(CN_VISIBLE);
3772        clsList.add(CN_HIDDEN);
3773      }
3774      cancel = true;
3775      break;
3776    case 't2_relations':
3777      var tbody = getPrntByIO(tgt, HTMLTableElement).querySelector('tbody');
3778      if (clsList.contains(CN_HIDDEN)) {
3779        clsList.remove(CN_HIDDEN);
3780        tbody.style.display = 'table-row-group';
3781        state.relationsCollapsed = false;
3782      } else {
3783        clsList.add(CN_HIDDEN);
3784        tbody.style.display = 'none';
3785        state.relationsCollapsed = true;
3786      }
3787      if (storage) storage.yardState = JSON.stringify(state);
3788      cancel = true;
3789      break;
3790    }
3791  }
3792
3793  if (!cancel && reCls.test(cls)) {
3794    if (reCls.test(tgt.parentElement.className)) {
3795      tgt = tgt.parentElement;
3796      cls = tgt.className;
3797    } else if (reCls.test(tgt.parentElement.parentElement.className)) {
3798      tgt = tgt.parentElement.parentElement;
3799      cls = tgt.className;
3800    }
3801  }
3802  cls = reCls.exec(tgt.className);
3803  if (!cancel && cls && cls[0]) {
3804    switch (cls[0]) {
3805    case 'toggleSource':
3806      var sourceCode = tgt.parentElement.parentElement.querySelector('div.source_code');
3807      if ( /^view/.test(tgt.textContent) ) {
3808        tgt.textContent = tgt.textContent.replace(/^view/, 'hide');
3809        sourceCode.classList.remove(CN_HIDDEN);
3810      } else {
3811        tgt.textContent = tgt.textContent.replace(/^hide/, 'view');
3812        sourceCode.classList.add(CN_HIDDEN);
3813      }
3814      cancel = true;
3815      break;
3816    case 'summary_signature':
3817      if (eA = tgt.querySelector('a')) {
3818        cancel = true;
3819        clickedBy = CB_CONTENT;
3820        clickedTop = tgt.getBoundingClientRect().top - fwHdrHgt;
3821        if (e.ctrlKey) {
3822          window.open(eA.href, '_blank');
3823        } else if (hash = eA.hash) {
3824          hash = decodeURIComponent(hash.slice(1));
3825          gotoDoc(eA.pathname + '#' + hash);
3826        } else gotoDoc(eA.href);
3827      }
3828      break;
3829    case 'h2_sum nodoc':
3830    case 'h2_sum':
3831      summaryToggle(tgt);
3832      cancel = true;
3833      break;
3834    case 'cnst_a':
3835      prnt = getPrntByIO(tgt, HTMLUListElement);
3836      // ul >> div >> h2
3837      clickedBy = CB_CONTENT;
3838      clickedTop = tgt.getBoundingClientRect().top - fwHdrHgt;
3839      if (e.ctrlKey) {
3840        window.open(tgt.href, '_blank');
3841        cancel = true;
3842      } else if (hash = tgt.hash) {
3843        hash = decodeURIComponent(hash.slice(1));
3844        prnt.parentElement.previousElementSibling.classList.remove(CN_HIDDEN);
3845        gotoDoc(tgt.pathname + '#' + hash);
3846      } else gotoDoc(tgt.href);
3847      cancel = true;
3848      break;
3849    }
3850  }
3851
3852  // Index page 'Listings'
3853  if (tag === 'LI' && (cls === 'r1' || cls === 'r2') ) {
3854    tgt = tgt.firstElementChild;
3855    tag = 'A';
3856  }
3857
3858  if (!cancel && !e.ctrlKey) {
3859    clickedTop = tgt.getBoundingClientRect().top - fwHdrHgt;
3860    if (tag === 'A') {
3861      eA = tgt;
3862      cancel = true;
3863    } else if (eA = tgt.parentElement) {
3864      if (eA instanceof HTMLAnchorElement) {
3865        cancel = true;
3866      } else if (eA = eA.parentElement) {
3867        if (eA instanceof HTMLAnchorElement) cancel = true;
3868      }
3869    }
3870    if (cancel && eA) {
3871      if (eA.className === 'repo') return true;
3872      clickedBy = CB_CONTENT;
3873      if (hash = eA.hash) {
3874        var a = eA.href.split('#');
3875        // console.log(a[0] + '#' + decodeURIComponent(a[1]));
3876        cancel = gotoDoc( a[0] + '#' + decodeURIComponent(a[1]) );
3877      } else
3878        cancel = gotoDoc(eA.href);
3879    }
3880  }
3881  if (cancel) {
3882    e.preventDefault();
3883    e.stopPropagation();
3884    return false;
3885  }
3886}
3887
3888function clkSettingWindow(e) {
3889  var tgt = e.target,
3890      tag = tgt.tagName,
3891      cls = tgt.className,
3892      id = tgt.id;
3893
3894  if (tag === 'SPAN' || tag === 'CODE'  || tag === 'svg') {
3895    // move up to button (code element used for the triangles)
3896    tgt = tgt.parentElement;
3897    tag = tgt.tagName;
3898    cls = tgt.className;
3899    id  = tgt.id;
3900  } else if (tag === 'rect' || tag === 'path') {
3901    tgt = tgt.parentElement.parentElement;
3902    tag = tgt.tagName;
3903    cls = tgt.className;
3904    id  = tgt.id;
3905  }
3906
3907  if (tgt instanceof HTMLButtonElement) {
3908    if (tgt.disabled) return;
3909    switch (id) {
3910      case 'font_lrg':
3911        changeFontSize(true);
3912        break;
3913      case 'font_sml':
3914        changeFontSize(false);
3915        break;
3916      default:
3917        oPane.mainClk(id);
3918        settingsSetData();
3919    };
3920  };
3921  e.preventDefault();
3922  e.stopPropagation();
3923  return false;
3924};
3925
3926/* Event handler for browser forward & back navigation events. */
3927function popState(e) {
3928  var loc;
3929  clickedBy = CB_POP_STATE;
3930  if (e.state) {
3931    gotoDoc(e.state.url);
3932  } else {
3933    loc = window.location;
3934    gotoDoc(loc.pathname + loc.hash);
3935  }
3936  e.preventDefault();
3937}
3938
3939/* Event handler for window keydown, used for Esc & Ctrl-Alt combinations. */
3940function winKeyDown(e) {
3941  var key = String.fromCharCode(e.which),
3942      keyU = key.toUpperCase(),
3943      keyD = key.toLowerCase(),
3944      isCtrlAlt = e.altKey && e.ctrlKey,
3945      url,
3946      cancel;
3947
3948  if (e.which === 27) {
3949    oPane.mainKD(keyD, 27);
3950    eContent.focus();
3951    cancel = true;
3952
3953  } else if (!isCtrlAlt) {
3954    return;
3955
3956  } else if (WIN_PANE_KD.indexOf(keyD) > -1 ) {
3957    oPane.mainKD(keyD, e.which);
3958    cancel = true;
3959
3960  } else {
3961    switch (keyD) {
3962    case 'i':
3963      var nl = document.querySelectorAll('#y_menu a');
3964      for (var i = 0, el; el = nl[i]; i++) {
3965        if (/^index/i.test(el.textContent)) {
3966          url = el.href;
3967          clickedBy = CB_OBJ_PATH;
3968          if ( /github\.io/i.test(url) && /\/_index\.html$/.test(url) )
3969            url = url.replace(/\/_index\.html$/, '/y_index.html');
3970          cancel = true;
3971          gotoDoc(url);
3972          break;
3973        }
3974      }
3975      break;
3976    }
3977  }
3978  if (cancel) {
3979    isWinKPTimeOut = true;
3980    e.preventDefault();
3981    e.stopPropagation();
3982    setTimeout( function(){ isWinKPTimeOut = false; }, 1000);
3983    return false;
3984  }
3985}
3986
3987/* Event handler window keypress, at present, only used for jumping locations in
3988 * the Method List.  Does not handle Ctrl-Alt, which uses winKeyDown.
3989 */
3990function winKeyPress(e) {
3991  var key = String.fromCharCode(e.which),
3992      cancel;
3993
3994  if (isWinKPTimeOut || document.activeElement === _id('list_search_text') ||
3995      document.activeElement === _id('toc_search_text') )
3996    return;
3997
3998  // Add main keyPress code here
3999
4000  if (e.altKey || e.ctrlKey) return;
4001
4002  if ( RE_LIST_SEARCH.test(key) ) {
4003    oList.keyPress(key);
4004    cancel = true;
4005  }
4006  if (cancel) {
4007    e.preventDefault();
4008    e.stopPropagation();
4009    return false;
4010  }
4011}
4012
4013/* Event handler for window resize. Sets up a timer so the screen refreshes are limited. */
4014function winResize() {
4015  var eAct = document.activeElement,
4016      contentS = eContent.style;
4017
4018  if (contentS && contentS.fontSize !== '') {
4019    //console.log('set_FS ' + contentS.fontSize + ' ' + (typeof contentS.fontSize));
4020    contentS.fontSize = '';
4021  }
4022
4023  if (isTouch && eAct && eAct.id === 'list_search_text' &&
4024      window.innerWidth === winWidth) {
4025    eAct.focus();
4026  } else if (winResizeTmrId) {
4027    window.clearTimeout(winResizeTmrId);
4028    winResizeTmrId = window.setTimeout( domLayout, 200);
4029  } else {
4030    winResizeTmrId = window.setTimeout( domLayout, 200);
4031  }
4032}
4033
4034//} @!endgroup
4035
4036//{ @!group Load & Layout
4037
4038/* Called on first load and anytime the window is resized */
4039function domLayout() {
4040  var winHeightLast = winHeight,
4041      winTypeLast = winType,
4042      charWid = 0,
4043      dockDisabled = false;
4044
4045// window.devicePixelRatio
4046
4047  e25vh    = _id('y_measure_vh');
4048  px30em   = _id('y_measure_em').offsetWidth;
4049  px50Char = _id('y_measure_50pre').offsetWidth;
4050
4051  winHeight = window.innerHeight;
4052  winWidth  = window.innerWidth;
4053
4054  charWid = Math.floor(50.0 * winWidth / px50Char);
4055
4056  if      (charWid > MED_LRG)  winType = 2;
4057  else if (charWid > SML_MED)  winType = 1;
4058  else { dockDisabled = true;  winType = 0; }
4059
4060//  if (winTypeLast !== winType && oList)
4061
4062  if (winHeight !== winHeightLast) {
4063//    if (isVHBad) vhFix();
4064    if (winHeight < clickedTop) {
4065      clickedTop = 0.6 * winHeight;
4066    }
4067  }
4068
4069  if (winType !== winTypeLast) {
4070    oPane.locSet(winTypeLast);
4071    if (_id('settings') && _id('settings').className === '') settingsSetData();
4072  };
4073  //  if (dbgYDebug) _id('y_debug').textContent = ' ' + charWid + '  ' + winType;
4074};
4075
4076/* Called anytime a new doc is loaded */
4077function docReady() {
4078  eContent.tabIndex = 1;
4079  _id('y_menu').addEventListener('click', clkMenu, false);
4080  eContent.addEventListener('click', clkContent, false);
4081}
4082
4083/* Hooks events on first doc load; these events are not affected when a doc
4084 * is loaded via XMLHttpRequest.
4085 */
4086function hookEvents() {
4087  window.addEventListener('keydown' , winKeyDown  , false);
4088  window.addEventListener('keypress', winKeyPress , false);
4089  window.addEventListener('resize'  , winResize   , false);
4090  window.addEventListener('popstate', popState    , false);
4091}
4092
4093/* Read options */
4094function readOptions() {
4095  if (_t2Info !== undefined) {
4096    if ('NSEP' in _t2Info)
4097      t2Opts.NSEP = _t2Info.NSEP.trim();
4098    if ('customHeaderId' in _t2Info)
4099      t2Opts.customHeaderId =  _t2Info.customHeaderId.trim();
4100  }
4101}
4102
4103/* Used for table overflow, see method --- */
4104function updateCSS() {
4105  var wid, t;
4106  if (oCSSRuleTable instanceof CSSRule) {
4107    wid = window.innerWidth;
4108    if (oList.d.docked && oList.d.vis)
4109      wid -= oList.e.getBoundingClientRect().width;
4110    if (oToc.d.docked && oToc.d.vis)
4111      wid -= oToc.e.getBoundingClientRect().width;
4112    wid -= 2.0 * (px30em)/30.0;
4113    oCSSRuleTable.maxWidth = Math.floor(wid) + 'px';
4114  };
4115};
4116
4117function setFontSize() {
4118  var rule = getCSSRule('y_style.css', 'html, body');
4119  if (rule && rule.style.fontSize !== state.fontSize)
4120    rule.style.fontSize = state.fontSize;
4121  px30em    = _id('y_measure_em').offsetWidth;
4122  px50Char  = _id('y_measure_50pre').offsetWidth;
4123  waitWidth = (isTouch ? WAIT_TOUCH : WAIT_NO_TOUCH ) * px30em/30.0;
4124
4125  if (!isFirstLoad) {
4126    settingsSetTitles();
4127    domLayout();
4128  };
4129};
4130
4131/* Changes cssRules to pixels for devices with bad vh
4132 * units, see isVHBad in {eDOMContent}
4133 */
4134function vhFix() {
4135  var i, j, maxH,
4136      fw_hdr = _id(t2Opts.customHeaderId),
4137      winH = winHeight - 2 - (fw_hdr ? fw_hdr.offsetHeight : 0),
4138      ss,
4139      cntr,
4140      cssRules,
4141      cssRule;
4142
4143  for (i = 0; ss = document.styleSheets[i]; i++) {
4144    if ( /y_list\.css$/.test(ss.href) ) {
4145      cssRules = ss.cssRules;
4146      cntr = 0;
4147      for (j = 0; cssRule = cssRules[j]; j++) {
4148        // style
4149        switch (cssRule.selectorText) {
4150          case '#y_list.f #list_nav':
4151            maxH = (winH - oList.hdrHeight - FLT_HGT * px30em / 30.0).toFixed(0);
4152            cssRule.style.maxHeight = maxH + 'px';
4153            cntr ++;
4154            break;
4155          case '#y_toc.f #toc_nav':
4156            maxH = (winH - oToc.hdrHeight - FLT_HGT * px30em / 30.0).toFixed(0);
4157            cssRule.style.maxHeight = maxH + 'px';
4158            cntr ++;
4159            break;
4160        }
4161        if (cntr === 2) break;
4162      }
4163    }
4164  }
4165}
4166
4167function svgAddPath(el, opacity, d) {
4168  var p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
4169  p.setAttribute('opacity', opacity);
4170  p.setAttribute('d', d);
4171  el.appendChild(p);
4172};
4173
4174/* Set html of SVG wait element, no need to have it in all of the content
4175 * pages
4176 */
4177function waitHTML() {
4178  var eWait = _id('y_wait'), el;
4179  if (isTouch) {
4180    eWait.setAttribute('viewBox', '0 0 135 45');
4181    eWait.setAttribute('width' , '135');
4182    eWait.setAttribute('height', '45');
4183    el = document.createElementNS('http://www.w3.org/2000/svg', 'text');
4184    el.setAttribute('opacity', '0.6');
4185    el.setAttribute('x', '67');
4186    el.setAttribute('y', '30');
4187    el.setAttribute('text-anchor', 'middle');
4188    el.textContent = 'Waiting...';
4189    eWait.appendChild(el);
4190  } else {
4191    eWait.setAttribute('viewBox', '0 0 90 90');
4192    eWait.setAttribute('width' , '90');
4193    eWait.setAttribute('height', '90');
4194
4195    svgAddPath(eWait, '0.1', 'M 58.46 7.28 L 62.16 8.81 52.59 31.91 48.89 30.38 ' +
4196      'M 71.87 15.3 L 74.7 18.13 57.02 35.81 54.19 32.98 ' +
4197      'M 81.19 27.84 L 82.72 31.54 59.62 41.11 58.09 37.41' +
4198      'M 85 43 L 85 47 60 47 60 43 ' +
4199      'M 82.72 58.46 L 81.19 62.16 58.09 52.59 59.62 48.89 ' +
4200      'M 74.7 71.87 L 71.87 74.7 54.19 57.02 57.02 54.19 ' +
4201      'M 62.16 81.19 L 58.46 82.72 48.89 59.62 52.59 58.09 ' +
4202      'M 47 85 L 43 85 43 60 47 60 ' +
4203      'M 31.54 82.72 L 27.84 81.19 37.41 58.09 41.11 59.62 ' +
4204      'M 18.13 74.7 L 15.3 71.87 32.98 54.19 35.81 57.02 ' +
4205      'M 8.81 62.16 L 7.28 58.46 30.38 48.89 31.91 52.59');
4206
4207    svgAddPath(eWait, '0.2', 'M 5 47 L 5 43 30 43 30 47');
4208    svgAddPath(eWait, '0.4', 'M 7.28 31.54 L 8.81 27.84 31.91 37.41 30.38 41.11');
4209    svgAddPath(eWait, '0.6', 'M 15.3 18.13 L 18.13 15.3 35.81 32.98 32.98 35.81');
4210    svgAddPath(eWait, '0.8', 'M 27.84 8.81 L 31.54 7.28 41.11 30.38 37.41 31.91');
4211    svgAddPath(eWait, '1.0', 'M 43 5 L 47 5 47 30 43 30');
4212  }
4213}
4214
4215function firstLoad() {
4216  var newToc,
4217      t, xhr, vhHgt,
4218      url, listState,
4219      oldLibRoot = oList.libRoot;
4220
4221  waitHTML = undefined;
4222  eContent = _id('content');
4223  e25vh    = _id('y_measure_vh');
4224//  px30em   = _id('y_measure_em').offsetWidth;
4225//  px50Char = _id('y_measure_50pre').offsetWidth;
4226
4227  isServer = (_id('y_menu').className === 'server');
4228  readOptions();
4229
4230  winTitle = document.querySelector('head title').textContent;
4231
4232  oPane.firstDOMLoad();
4233
4234  aDOMCur.href  = window.location.href;
4235  aDOMNext.href = window.location.href;
4236
4237  for (var i = 0, cssRules, ss; ss = document.styleSheets[i]; i++) {
4238    if ( /y_style\.css$/.test(ss.href)) {
4239      cssRules = ss.cssRules;
4240      cntr = 0;
4241      for (var j = 0, cssRule; cssRule = cssRules[j]; j++) {
4242        if (cssRule.selectorText === 'div.y_table') {
4243          oCSSRuleTable = cssRule;
4244          break;
4245        }
4246      }
4247    }
4248  }
4249
4250  vhHgt = (4 * e25vh.getBoundingClientRect().width).toFixed(0);
4251  winHeight = window.innerHeight;
4252
4253  //hdrDbg('v' + vhHgt + '_w' + winHeight);
4254
4255  // for iOS
4256  if ( Math.abs(winHeight - vhHgt)/winHeight > 0.01 || t2Opts.customHeaderId ) {
4257    t = _id(t2Opts.customHeaderId);
4258    if (t) fwHdrHgt = t.offsetHeight;
4259    isVHBad = true;
4260    // hdrDbg('v' + vhHgt + '_w' + winHeight + ' called vhFix');
4261    vhFix();
4262  }
4263
4264  domLayout();
4265  updateCSS();
4266  oList.e.tabIndex = 2;
4267
4268  // must run before addContent
4269  if (t2GA instanceof Function && !t2GAInit) {
4270    t2GA( 'create', 'UA-79746991-1', 'auto');
4271    t2GA( 'set', 'anonymizeIp', true);
4272    t2GAInit = true;
4273  }
4274
4275  newToc = addContent(eContent, document);
4276  oToc.nav.replaceChild(newToc.cloneNode(true), oToc.items);
4277  oToc.items = _id('toc_items');
4278  newToc = null;
4279
4280  hookEvents();
4281  docReady();
4282  window.removeEventListener('DOMContentLoaded', eDOMContent);
4283  hookEvents = undefined;
4284  isFirstLoad = false;
4285}
4286
4287/* Event handler for DOMContentLoaded */
4288function eDOMContent(e) {
4289  eContent = _id('content');
4290  setFontSize();
4291  waitHTML();
4292  commonLoad();
4293  firstLoad();
4294
4295  if ( typeof hljs !== 'undefined' && hljs.highlightElement instanceof Function ) {
4296    hljs.configure({
4297      ignoreUnescapedHTML: true
4298    });
4299  };
4300}
4301
4302//} @!endgroup
4303
4304window.addEventListener('DOMContentLoaded', eDOMContent, false);
4305
4306})(t2Info);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.