PageSourceSearch

https://steyr-automotive.com/fileadmin/Steyr2024/Resources/Public/…/Js/responsive-table.js?1706182765

js steyr-automotive.com collected 2026-10-02 06:02:04 UTC 26,644 bytes, 816 lines download raw bytes

1/**
2 * responsive-table.js
3 *
4 * Responsive Table is a tasty JavaScript library which helps format data tables on small
5 screens! It does so by reordering items in the DOM while keeping HTML markup
6 valid and assistive technology friendly!
7 *
8 * Repository URL: https://github.com/svinkle/responsive-table
9 *
10 * @author: Scott Vinkle <[email protected]>
11 * @version: 0.4.1
12 */
13var ResponsiveTable = (function(window, document, undefined) {
14  'use strict';
15
16  // Unique table identifier.
17  var tableId = 0;
18
19  // Lookup table of selectors, either id or element, no classes.
20  var selectors = {
21    tableList: 'table-list',
22    selected: 'selected-list-item',
23    tableRows: 'tbody tr',
24    tableCells: 'tbody td',
25    tableHeading: 'table-heading',
26    windowLink: 'window-link',
27    columnHeaders: 'thead th[scope="col"]',
28    rowHeaders: 'tbody th[scope="row"]',
29    caption: 'caption',
30    h2: 'h2',
31    links: 'a',
32    span: 'span',
33    styleSheets: 'link[rel="stylesheet"]',
34    tableRowFocusElement: 'tableRowFocusElement'
35  };
36
37  // Lookup table of classes, to apply styles only.
38  var classes = {
39    table: 'responsive-table',
40    caption: 'responsive-table__caption',
41    tableList: 'responsive-table__list',
42    tableListItem: 'responsive-table__list-item',
43    tableListLink: 'responsive-table__list-link',
44    windowLink: 'responsive-table__window-link',
45    windowLinkActive: 'responsive-table__window-link--active',
46    windowLinkIcon: 'responsive-table__window-link-icon',
47    windowBody: 'responsive-table__window-body',
48    tableRowActive: 'responsive-table__row--active',
49    tableSmallScreen: 'responsive-table--small-screen',
50    headerContent: 'responsive-table__header-content',
51    cellContent: 'responsive-table__cell-content',
52    hidden: 'hidden',
53    visuallyHidden: 'visuallyhidden'
54  };
55
56  // Lookup table of common, unique strings.
57  var strings = {
58    tableMissing: 'Table selector missing from object.',
59    tableNotFound: 'Table not found: ',
60    tableTypeMissing:
61      'No type defined. Must be "list", "stack", or "window" for table: ',
62    windowLinkStart: 'Open ',
63    windowLinkEnd: ' table in a new window',
64    windowIcon:
65      '<svg class="' +
66      classes.windowLinkIcon +
67      '" width="32" height="32" viewBox="0 0 32 32"><g transform="scale(0.03125 0.03125)"><path d="M192 64v768h768v-768h-768zM896 768h-640v-640h640v640zM128 896v-672l-64-64v800h800l-64-64h-672z"></path><path d="M352 256l160 160-192 192 96 96 192-192 160 160v-416z"></path></g></svg>',
68    checkDom: 'Check document structure requirements: ',
69    cellCount: 'Column/row cell count mismatch: ',
70    selected: ', selected',
71    headerContent: 'header-content-',
72    cellContent: 'cell-content-',
73    auto: 'auto',
74    column: 'column',
75    row: 'row',
76    show: 'show',
77    hide: 'hide',
78    large: 'large',
79    small: 'small',
80    list: 'list',
81    stack: 'stack',
82    window: 'window',
83    dataListLink: 'data-list-link',
84    pageSettings: {
85      lang: 'en',
86      charset: '<meta charset="UTF-8">',
87      httpEquiv:
88        '<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">',
89      viewport:
90        '<meta name="viewport" content="width=device-width, initial-scale=1.0">'
91    }
92  };
93
94  // Class constructor.
95  var ResponsiveTable = function(table, type, breakPoint) {
96    var thisTable = null,
97      thisTableId = null,
98      tableList = null,
99      mediaQuery = null;
100
101    // Check to see if the `table` param is present.
102    // Then check to see if it exists in the DOM.
103    // If all good, assign the element to `thisTable`.
104    if (!table) {
105      console.error(strings.tableMissing);
106      return;
107    } else if (!document.querySelector(table)) {
108      console.error(strings.tableNotFound + table);
109      return;
110    } else {
111      thisTable = document.querySelector(table);
112    }
113
114    // Check to see if the `type` param is present.
115    // If all good, assign the value to `tableType`.
116    if (!type) {
117      console.error(strings.tableTypeMissing + table);
118      return;
119    }
120
121    /**
122     * Gets the current `table` element.
123     *
124     * @return thisTable {Object} the `table` element.
125     */
126    this.getTable = function() {
127      return thisTable;
128    };
129
130    /**
131     * Sets a unique identifier for the current `table`.
132     */
133    this.setTableId = function() {
134      thisTableId = tableId;
135    };
136
137    /**
138     * Gets the unique identifier value of the current table.
139     *
140     * @return thisTableId {number} index of the current `table`.
141     */
142    this.getTableId = function() {
143      return thisTableId;
144    };
145
146    /**
147     * Gets the current table media query object to test against.
148     *
149     * @return mediaQuery {Object} `matchMedia` window object.
150     */
151    this.getMediaQuery = function() {
152      return mediaQuery;
153    };
154
155    /**
156     * For `list` type tables, sets the current list `ul` element.
157     */
158    this.setTableList = function(list) {
159      tableList = list;
160    };
161
162    /**
163     * Gets the current list `ul` element.
164     *
165     * @return tableList {Object} the `ul` element.
166     */
167    this.getTableList = function() {
168      return tableList;
169    };
170
171    /**
172     * Gets the current table type.
173     *
174     * @return type {String} the value of the desired table type.
175     */
176    this.getTableType = function() {
177      return type;
178    };
179
180    /**
181     * Gets the current `table` selector.
182     *
183     * @return table {String} the `id` attribute from the current `table`.
184     */
185    this.getTableSelector = function() {
186      return table;
187    };
188
189    // Add classes to the table, etc, for styling puposes only.
190    thisTable.classList.add(classes.table);
191    thisTable.classList.add(classes.table + '--' + type);
192    thisTable.querySelector(selectors.caption).classList.add(classes.caption);
193
194    // Set the unique identifier for the current table, then increment for
195    // the next `table`.
196    this.setTableId();
197    tableId++;
198
199    // Call `checkStructure()` method to make sure everything's in the DOM
200    // before continuing on.
201    if (this.checkStructure()) {
202
203      // Create the `matchMedia` object to test the desired `breakpoint`
204      // value against.
205      mediaQuery = window.matchMedia(
206        '(min-width: ' +
207          (!breakPoint
208            ? window.getComputedStyle(thisTable, null).width
209            : breakPoint) +
210          ')'
211      );
212
213      // Add the event listener to the `matchMedia` object, listen
214      // for any window resizing or device orientation changes.
215      mediaQuery.addListener(
216        ResponsiveTable.prototype.testMediaQuery.bind(this)
217      );
218
219      // Test the media query on page load to adjust the `table` if
220      // required.
221      this.testMediaQuery();
222    }
223  };
224
225  /**
226   * Checks the `table` elements and structure to make sure everything
227   * is in place. If something is missing, an error message is displayed
228   * in the `console` window.
229   *
230   * @method checkStructure
231   * @return {Boolean} simply return `true` or `false`.
232   */
233  ResponsiveTable.prototype.checkStructure = function() {
234    var thisTable = this.getTable(),
235      thisTableType = this.getTableType(),
236      columnHeaders = thisTable.querySelectorAll(selectors.columnHeaders),
237      columnHeadersLength = columnHeaders.length,
238      tableRows = thisTable.querySelectorAll(selectors.tableRows),
239      tableRowsLength = tableRows.length,
240      structureError = false,
241      errorMessage = '',
242      i = 0;
243
244    // The `list` style requires `scope="col"` and `scope="row"` headers.
245    // The `stack` or `window` styles only require `scope="col"` headers.
246    // A `caption` is also required to generate the heading.
247    if (thisTableType === strings.list) {
248      if (
249        !thisTable.querySelector(selectors.columnHeaders) ||
250        !thisTable.querySelector(selectors.rowHeaders) ||
251        !thisTable.querySelector(selectors.caption)
252      ) {
253        structureError = true;
254        errorMessage = strings.checkDom;
255      } else {
256
257        // Check to make sure each row cell count matches the total
258        // column count. Otherwise, `list` tables will output
259        // mismatched data.
260        for (; i < tableRowsLength; i++) {
261          if (tableRows[i].children.length !== columnHeadersLength) {
262            structureError = true;
263            errorMessage =
264              strings.cellCount +
265              '"' +
266              tableRows[i].children[0].textContent +
267              '": ';
268          }
269        }
270      }
271    } else if (
272      thisTableType === strings.stack ||
273      thisTableType === strings.window
274    ) {
275      if (
276        !thisTable.querySelector(selectors.columnHeaders) ||
277        !thisTable.querySelector(selectors.caption)
278      ) {
279        structureError = true;
280        errorMessage = strings.checkDom;
281      }
282    }
283
284    // If anything is missing, send an error message to the `console` window
285    // and stop anything else from happening.
286    if (structureError) {
287      console.error(errorMessage + this.getTableSelector());
288      return false;
289    } else {
290      return true;
291    }
292  };
293
294  /**
295   * Check and see if the desired breakpoint has been matched, and setup
296   * the `table` accordingly for the desired style.
297   *
298   * @method testMediaQuery
299   */
300  ResponsiveTable.prototype.testMediaQuery = function() {
301    if (this.getMediaQuery().matches) {
302      this.setupScreen(strings.large);
303    } else {
304      this.setupScreen(strings.small);
305    }
306  };
307
308  /**
309   * Call the appropriate methods to re-arrage the `table` based on the
310   * current screen "size" and selected `table` theme.
311   *
312   * @method setupScreen
313   * @param size {String} the "size" of the current screen.
314   */
315  ResponsiveTable.prototype.setupScreen = function(size) {
316    var thisTable = this.getTable(),
317      thisTableType = this.getTableType(),
318      tableContainer = thisTable.parentNode,
319      caption = thisTable.querySelector(selectors.caption);
320
321    // The `list` style needs to show/hide each row, and display an
322    // "active" row. After, the link list is generated to click betweeen
323    // `table` rows.
324    if (thisTableType === strings.list) {
325      if (size === strings.small) {
326        this.toggleActiveRow(strings.hide);
327        this.generateList();
328      } else {
329        this.toggleActiveRow(strings.show);
330        this.removeList();
331      }
332    }
333
334    // The `list` and `stack` styles need to show/hide the header elements
335    // as well as move content around to meet their theme requirements.
336    // The `tableSmallScreen` CSS class is added for styling purposes.
337    if (thisTableType === strings.list || thisTableType === strings.stack) {
338      if (size === strings.small) {
339        thisTable.classList.add(classes.tableSmallScreen);
340        this.toggleHeaders(strings.hide);
341        this.moveContent();
342      } else {
343        thisTable.classList.remove(classes.tableSmallScreen);
344        this.toggleHeaders(strings.show);
345        this.resetContent();
346      }
347    }
348
349    // The `window` types need to show/hide the link which opens the `table`
350    // in its own window.
351    if (thisTableType === strings.window) {
352
353      // Need to add a heading before table so open link has context
354      if (!tableContainer.querySelector('h2')) {
355        var tableHeading = document.createElement('h2');
356        tableHeading.id = selectors.tableHeading + '-' + tableId;
357        tableHeading.textContent = caption.textContent;
358        tableContainer.insertBefore(tableHeading, tableContainer.firstChild);
359        caption.classList.add(classes.visuallyHidden);
360      }
361
362      this.displayWindowLink(size === 'small' ? true : false);
363    }
364  };
365
366  /**
367   * Called to show/hide `table` rows for `list` style tables.
368   *
369   * @method toggleActiveRow
370   * @param toggle {String} determines the display of all or one row
371   */
372  ResponsiveTable.prototype.toggleActiveRow = function(toggle) {
373    var tableRows = this.getTable().querySelectorAll(selectors.tableRows),
374      tableRowsLength = tableRows.length,
375      i = 0;
376
377    // Hide or show all rows, depending on the `toggle` value.
378    for (; i < tableRowsLength; i++) {
379      if (toggle === strings.hide) {
380        tableRows[i].classList.add(classes.hidden);
381      } else {
382        tableRows[i].classList.remove(classes.hidden);
383      }
384    }
385
386    // On initial load, when hiding rows, only show the first row and mark
387    // it as `active`. Otherwise, show all rows.
388    if (toggle === strings.hide) {
389      tableRows[0].classList.remove(classes.hidden);
390      tableRows[0].classList.add(classes.tableRowActive);
391    } else {
392      tableRows[0].classList.remove(classes.tableRowActive);
393    }
394  };
395
396  /**
397   * Generate the link list for `list` themed tables. Links are used to
398   * show the associated row's content.
399   *
400   * @method generateList
401   */
402  ResponsiveTable.prototype.generateList = function() {
403    var thisTable = this.getTable(),
404      tableList = this.getTableList(),
405      rowHeaders = thisTable.querySelectorAll(selectors.rowHeaders),
406      rowHeadersLength = rowHeaders.length,
407      newSpanWrapper = null,
408      rowHeader = null,
409      list = null,
410      listItem = null,
411      listLink = null,
412      i = 0,
413      j = 0;
414
415    // If the link list already exists in the DOM, show it. Otherwise,
416    // create a new link list.
417    if (tableList) {
418      tableList.classList.remove(classes.hidden);
419    } else {
420
421      // Create the link list element and set attributes
422      list = document.createElement('ul');
423
424      list.id = selectors.tableList;
425      list.classList.add(classes.tableList);
426
427      // Create links using the `scope="row"` header text, then add the
428      // link and list item to the `ul` list element.
429      for (; i < rowHeadersLength; i++) {
430        listItem = document.createElement('li');
431        listLink = document.createElement('a');
432        rowHeader = rowHeaders[i];
433        j = 0;
434
435        listItem.classList.add(classes.tableListItem);
436
437        listLink.href = '#' + selectors.tableRowFocusElement + i;
438        listLink.textContent = rowHeader.textContent;
439        listLink.classList.add(classes.tableListLink);
440        listLink.setAttribute(strings.dataListLink, i);
441        listLink.addEventListener(
442          'click',
443          ResponsiveTable.prototype.listLinkClick.bind(this),
444          false
445        );
446
447        listItem.appendChild(listLink);
448        list.appendChild(listItem);
449
450        // wrap header elements in a new span element that we can later give focus to.
451        // create new span element
452        newSpanWrapper = document.createElement('span');
453        newSpanWrapper.id = selectors.tableRowFocusElement + i;
454
455        // Move all header nodes into the new span
456        for (; j < rowHeader.childNodes.length; j++) {
457          newSpanWrapper.appendChild(rowHeader.childNodes[0]);
458        }
459
460        // Add span back into header
461        rowHeader.insertBefore(newSpanWrapper, rowHeader.firstChild);
462      }
463
464      // Add the `ul` element to the DOM, directly before the `table`.
465      thisTable.parentNode.insertBefore(list, thisTable);
466
467      // Set this list element as the link list for the current `table`.
468      this.setTableList(list);
469    }
470  };
471
472  /**
473   * Hide the link list element for `list` themed tables from the DOM.
474   *
475   * @method removeList
476   */
477  ResponsiveTable.prototype.removeList = function() {
478    var tableList = this.getTableList();
479
480    // If the link list doesn't exist in the DOM, don't continue.
481    if (!tableList) {
482      return;
483    }
484
485    // Hide the link list.
486    tableList.classList.add(classes.hidden);
487  };
488
489  /**
490   * Show or hide `table` headers per the desired theme.
491   *
492   * @method toggleHeaders
493   * @param toggle {String} determines the display of `table` headers.
494   */
495  ResponsiveTable.prototype.toggleHeaders = function(toggle) {
496    var columnHeaders = this.getTable().querySelectorAll(
497        selectors.columnHeaders
498      ),
499      columnHeadersLength = columnHeaders.length,
500      thisHeader = null,
501      headerRow = null,
502      i = 0;
503
504    // Show/hide each `scope="col"` header and its parent `tr` element.
505    for (; i < columnHeadersLength; i++) {
506      thisHeader = columnHeaders[i];
507      headerRow = thisHeader.parentNode;
508
509      if (toggle === strings.hide) {
510        thisHeader.classList.add(classes.hidden);
511        headerRow.classList.add(classes.hidden);
512      } else {
513        thisHeader.classList.remove(classes.hidden);
514        headerRow.classList.remove(classes.hidden);
515      }
516    }
517  };
518
519  /**
520   * Move content around the DOM for "small" screens in order to meet the
521   * theme requirements.
522   *
523   * @method moveContent
524   */
525  ResponsiveTable.prototype.moveContent = function() {
526    var thisTable = this.getTable(),
527      tableId = this.getTableId(),
528      tableContainer = thisTable.parentNode,
529      tableCells = thisTable.querySelectorAll(selectors.tableCells),
530      tableCellsLength = tableCells.length,
531      colunmHeaders = thisTable.querySelectorAll(selectors.columnHeaders),
532      colunmHeadersLength = colunmHeaders.length - 1,
533      hasRowHeaders = thisTable.querySelectorAll(selectors.rowHeaders).length,
534      caption = thisTable.querySelector(selectors.caption),
535      tableHeading = null,
536      tableHeadingContent = null,
537      cellContent = null,
538      thisCell = null,
539      i = 0,
540      j = hasRowHeaders ? 1 : 0;
541
542    for (; i < tableCellsLength; i++) {
543      thisCell = tableCells[i];
544
545      // Get the current `scope="col"` element text to be used within the
546      // cell content template, taking in to consideration that some
547      // headers may be empty cells.
548      if (colunmHeaders[j] !== undefined) {
549        tableHeadingContent = colunmHeaders[j].textContent;
550      }
551
552      // Make a backup copy of the current cell content.
553      cellContent = thisCell.innerHTML;
554
555      // Clear the cell content and insert the header text along side the
556      // current cell content, per theme requirements.
557      thisCell.innerHTML = '';
558      thisCell.innerHTML =
559        '<span id="' +
560        strings.headerContent +
561        i +
562        '" class="' +
563        classes.headerContent +
564        '">' +
565        tableHeadingContent +
566        '</span>: <span id="' +
567        strings.cellContent +
568        i +
569        '" class="' +
570        classes.cellContent +
571        '">' +
572        cellContent +
573        '</span>';
574
575      // Increment the counter to grab the current cell's `scope="col"`
576      // element content. Reset the counter when we've reached the
577      // end of the row to grab the first column content and start again.
578      j = j >= colunmHeadersLength ? (j = hasRowHeaders ? 1 : 0) : j + 1;
579    }
580
581    // If the `table` heading element exists, show it. Otherwise, create
582    // a new heading element and insert it before the `table`. Use the
583    // `caption` element text as the heading text.
584    // (The purpose for the heading is mostly used to get around a
585    // VoiceOver bug, where VO doesn't move in to a `table` when focused.)
586    if (
587      tableContainer.querySelector('#' + selectors.tableHeading + '-' + tableId)
588    ) {
589      tableContainer
590        .querySelector('#' + selectors.tableHeading + '-' + tableId)
591        .classList.remove(classes.hidden);
592    } else {
593      tableHeading = document.createElement('h2');
594
595      tableHeading.id = selectors.tableHeading + '-' + tableId;
596      tableHeading.textContent = caption.textContent;
597
598      tableContainer.insertBefore(tableHeading, tableContainer.firstChild);
599    }
600
601    // Hide the caption since the heading exists now.
602    caption.classList.add(classes.visuallyHidden);
603  };
604
605  /**
606   * Reset the content back to its original, full-width `table` formatting.
607   *
608   * @method resetContent
609   */
610  ResponsiveTable.prototype.resetContent = function() {
611    var thisTable = this.getTable(),
612      tableId = this.getTableId(),
613      caption = thisTable.querySelector(selectors.caption),
614      tableContainer = thisTable.parentNode,
615      tableCells = thisTable.querySelectorAll(selectors.tableCells),
616      tableCellsLength = tableCells.length,
617      thisCell = null,
618      thisCellContent = '',
619      i = 0;
620
621    for (; i < tableCellsLength; i++) {
622      thisCell = tableCells[i];
623
624      // Make sure the content we're looking to place back actually exists.
625      if (thisCell.querySelector('#' + strings.headerContent + i) !== null) {
626
627        // Make a backup copy of the current cell content, minus the
628        // header content.
629        thisCellContent = thisCell.querySelector('#' + strings.cellContent + i)
630          .innerHTML;
631
632        // Remove the header and cell content elements.
633        thisCell.querySelector('#' + strings.headerContent + i).remove();
634        thisCell.querySelector('#' + strings.cellContent + i).remove();
635
636        // Place the cell content back in to the cell.
637        thisCell.innerHTML = thisCellContent;
638      }
639    }
640
641    // If it exists in the DOM, hide the `table` heading.
642    if (
643      tableContainer.querySelector('#' + selectors.tableHeading + '-' + tableId)
644    ) {
645      tableContainer
646        .querySelector('#' + selectors.tableHeading + '-' + tableId)
647        .classList.add(classes.hidden);
648    }
649
650    // Show the caption.
651    caption.classList.remove(classes.visuallyHidden);
652  };
653
654  /**
655   * Click event for each `list` themed list link item.
656   *
657   * @method listLinkClick
658   * @param event {Object} click event information object.
659   */
660  ResponsiveTable.prototype.listLinkClick = function(event) {
661    event.preventDefault();
662
663    var thisTable = this.getTable(),
664      thisTableList = this.getTableList(),
665      thisLink = event.target,
666      listLinks = thisTableList.querySelectorAll(selectors.links),
667      listLinksLength = listLinks.length,
668      tableRows = thisTable.querySelectorAll(selectors.tableRows),
669      tableRowsLength = tableRows.length,
670      thisTableRow = parseInt(
671        event.target.getAttribute(strings.dataListLink),
672        10
673      ),
674      selectedText =
675        '<span class="' +
676        classes.visuallyHidden +
677        '">' +
678        strings.selected +
679        '</span>',
680      i = 0,
681      j = 0;
682
683    // Remove the `active` class and hide each row.
684    // Show the `table` row which index matches the clicked link's
685    // `data-list-link` value.
686    for (; i < tableRowsLength; i++) {
687      tableRows[i].classList.remove(classes.tableRowActive);
688      tableRows[i].classList.add(classes.hidden);
689
690      if (i === thisTableRow) {
691        tableRows[i].classList.remove(classes.hidden);
692        tableRows[i].classList.add(classes.tableRowActive);
693      }
694    }
695
696    // Remove the "selected" hidden text from each link.
697    for (; j < listLinksLength; j++) {
698      if (listLinks[j].querySelector(selectors.span)) {
699        listLinks[j].querySelector(selectors.span).remove();
700      }
701    }
702
703    // Apply the text to the clicked link element.
704    thisLink.innerHTML = thisLink.textContent + selectedText;
705
706    // Get link href #value to know where to set focus
707    var visibleRowHeaderTarget = thisTable.querySelector(
708      '#' + thisLink.hash.substr(1)
709    );
710
711    // Set the `table` heading to be focusable, then set focus.
712    visibleRowHeaderTarget.setAttribute('tabindex', -1);
713    visibleRowHeaderTarget.focus();
714  };
715
716  /**
717   * Click event for the `window` themed "open window" link.
718   *
719   * @method windowLinkClick
720   * @param event {Object} click event information object.
721   */
722  ResponsiveTable.prototype.windowLinkClick = function(event) {
723    event.preventDefault();
724
725    var thisTable = this.getTable(),
726      tableWindow = window.open(),
727      tableWindowDocument = tableWindow.document,
728      tableContent = thisTable.outerHTML,
729      styleSheets = document.head.querySelectorAll(selectors.styleSheets),
730      styleSheetsLength = styleSheets.length,
731      tableWindowStyleSheets = null,
732      i = 0;
733
734    // Set the language of the new window document.
735    tableWindowDocument.documentElement.lang = strings.lang;
736
737    // Set the character set, httpEquiv value, and viewport meta tags of the
738    // new window document.
739    tableWindowDocument.head.innerHTML =
740      strings.pageSettings.charset +
741      strings.pageSettings.httpEquiv +
742      strings.pageSettings.viewport;
743
744    // Set the `title` element of the new window document to that of the
745    // current `caption` value.
746    tableWindowDocument.title = thisTable.querySelector(
747      selectors.caption
748    ).textContent;
749
750    // Dynamically create `link` elements in order to embed the current
751    // page's stylesheets, and append to the `head` element of the new
752    // window document.
753    for (; i < styleSheetsLength; i++) {
754      tableWindowStyleSheets = tableWindowDocument.createElement('link');
755
756      tableWindowStyleSheets.rel = 'stylesheet';
757      tableWindowStyleSheets.href = styleSheets[i].href;
758
759      tableWindowDocument.head.appendChild(tableWindowStyleSheets);
760    }
761
762    // Set the entire `body` element content of the new window document to
763    // that of the current `table` element content.
764    tableWindowDocument.body.innerHTML = tableContent;
765    tableWindowDocument.body.classList.add(classes.windowBody);
766
767    // Remove visuallyhidden class on table caption
768    var newWindowCaption = tableWindowDocument.querySelector(selectors.caption);
769    newWindowCaption.classList.remove(classes.visuallyHidden);
770  };
771
772  /**
773   * Show/hide the `window` theme "open window" link.
774   *
775   * @method displayWindowLink
776   * @param display {Boolean} determines display mode of the "open window" link
777   */
778  ResponsiveTable.prototype.displayWindowLink = function(display) {
779    var thisTable = this.getTable(),
780      tableHeader = thisTable.parentNode.querySelector(selectors.h2),
781      windowLink = document.querySelector('#' + selectors.windowLink);
782
783    // On display, create the link and set its parameters and event
784    // listener, the insert before the `table`. Otherwise, remove the link
785    // from the DOM.
786    if (display) {
787      windowLink = document.createElement('a');
788
789      windowLink.id = selectors.windowLink;
790      windowLink.href = '#';
791      windowLink.classList.add(classes.windowLink);
792      windowLink.innerHTML =
793        '<span class="' +
794        classes.visuallyHidden +
795        '">' +
796        strings.windowLinkStart +
797        tableHeader.textContent +
798        strings.windowLinkEnd +
799        '</span>' +
800        strings.windowIcon;
801      windowLink.addEventListener(
802        'click',
803        ResponsiveTable.prototype.windowLinkClick.bind(this),
804        false
805      );
806
807      thisTable.parentNode.insertBefore(windowLink, tableHeader.nextSibling);
808    } else {
809      if (windowLink) {
810        windowLink.remove();
811      }
812    }
813  };
814
815  return ResponsiveTable;
816})(window, document);

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.