PageSourceSearch

http://icehe.me/js/tableef7d.js

js icehe.me collected 2026-09-28 07:14:10 UTC 21,653 bytes, 784 lines download raw bytes

1// EasyChair source script
2// (c) 2010-2016 easychair.org
3// (c) 2018-2019,2023 EasyChair Ltd
4
5/** @typedef {{head?: HTMLElement, rows: HTMLElement[]}} Section */
6
7function Tab(id,uniqueId,hasHeader,hasSummary,hashScroll)
8{
9  try {
10    this.id = id;
11    /** Instead of array with rows we use array with sections. Every
12     * section is an array with rows.
13     * @type {Section[]} */
14    this.sections = [];
15    this.numberOfRows = 0;
16    Tab.tables[id] = this;
17    this.navigation = false; // if true, has navigation bar
18    // this is the table state
19    this.inc = false; // if true, then sorting in an increasing order
20    this.currentColumn = -1; // current sort column
21    this.page = 1;
22    this.displayRows = 50;
23    this.search = '';
24    this.drawing = false;
25    this.uniqueId = uniqueId;
26    this.hasHeader = hasHeader;
27    this.hasSummary = hasSummary;
28    this.hashScroll = hashScroll;
29  }
30  catch (err) {
31    logError(err,'Tab.new');
32  }
33} // Tab
34
35Tab.tables = {};
36
37Tab.init = function()
38{
39  try {
40    Tab.findTables();
41    var iter = values(Tab.tables).iterator();
42    var hashScroll = false;
43    while (iter.hasNext()) {
44      var tab = iter.next();
45      tab.initialize();
46      tab.restoreState();
47      hashScroll = hashScroll || tab.hashScroll;
48    }
49    Tab.initialized = true;
50    if (hashScroll) {
51      addEventHandler(window, 'hashchange', Tab.hashChanged);
52      // scroll element into view if the page hash contains an element name
53      Tab.hashChanged2(location.hash);
54    }
55  }
56  catch (err) {
57    logError(err,'Tab.init');
58  }
59} // Tab.init
60
61/**
62 * Find all new kind tables on the page and process it.
63 */
64Tab.findTables = function () {
65  try {
66    var tables = [];
67    var allTables = document.getElementsByTagName('table');
68    for (var t = 0; t < allTables.length; ++t) {
69      var table = allTables[t];
70      if (table.getAttribute('data-kind') === 'new') {
71        var id = table.id;
72        var uid = table.getAttribute('data-uid');
73        var hasHeader = table.getAttribute('data-header') === 'true';
74        var hasSummary = table.getAttribute('data-summary') === 'true';
75        var hashScroll = table.getAttribute('data-scroll') === 'true';
76
77        var sort = {};
78        var headersColumn = table.getElementsByTagName('th');
79        var header = null;
80        // need to find the header with sort columns.
81        for (var i = 0; i < headersColumn.length; ++i) {
82          var cell = headersColumn[i];
83          if (cell.hasAttribute('data-sort')) {
84            header = cell.parentElement;
85            break;
86          }
87        }
88
89        // add listeners to found header
90        if (header) {
91          for (var i = 0; i < header.children.length; ++i) {
92            var column = header.children[i];
93            var sortKind = column.getAttribute('data-sort');
94            if (sortKind) {
95              sort[i + 1] = sortKind;
96              column.addEventListener('click', Tab.sortColumn.bind(null, id, i + 1));
97            }
98          }
99        }
100
101        new Tab(id, uid, hasHeader, hasSummary, hashScroll).setSort(sort);
102      }
103    }
104  } catch (err) {
105    logError(err, 'Tab.findTables');
106  }
107} // findTables
108
109Tab.saveAll = function()
110{
111  try {
112    var iter = values(Tab.tables).iterator();
113    while (iter.hasNext()) {
114      iter.next().saveState();
115    }
116  }
117  catch (err) {
118    logError(err,'Tab.saveAll');
119  }
120} // Tab.saveAll
121
122Tab.sortColumn = function(tableId,columnNumber)
123{
124  try {
125    if (!Tab.initialized) {
126      Tab.notFullyLoaded();
127      return;
128    }
129    Tab.tables[tableId].sortOn(columnNumber);
130  }
131  catch (err) {
132    logError(err,'Tab.sortColumn');
133  }
134} // Tab.sortColumn
135
136Tab.notFullyLoaded = function()
137{
138  alert('It seems that the table was not fully loaded, so this operation cannot be performed.');
139} // Tab.notFullyLoaded
140
141Tab.prototype = {
142  initialize : function()
143  {
144    try {
145      var tab = $(this.id);
146      /** @type {HTMLCollection} */
147      var rows = tab.rows;
148      if (!rows) { // browser bugs???
149        return;
150      }
151
152      var entriesField = $(this.id + ':entries');
153      if (entriesField) {
154        this.displayRows = EC.getSelection(entriesField);
155        if (this.displayRows !== 'all') {
156          this.displayRows = Number(this.displayRows);
157        }
158        this.navigation = true;
159      }
160
161      // add attributes for the primary sort
162      var startRow = this.navigation ? 1 : 0;
163      if (this.hasHeader) {
164        startRow++;
165      }
166      var endRow = rows.length;
167      if (this.hasSummary) {
168        endRow--;
169      }
170      /** @type {Section} */
171      var section = {
172        rows: []
173      };
174      for (var n = startRow; n < endRow; n++) {
175        var row = rows[n];
176        addDebug('init_n',n);
177        addDebug('init_r',row ? 1 : 0);
178        if (row.getAttribute('data-ignore') !== null) {
179          if (section.head || section.rows.length) {
180            this.sections.push(section);
181            section = {
182              rows: []
183            };
184          }
185          section.head = row;
186          continue;
187        }
188        section.rows.push(row);
189        this.numberOfRows++;
190
191        row['ec:prim'] = this.navigation ? n - 1 : n;
192
193        var cells = row.cells;
194        for (var c = cells.length - 1; c >= 0; c--) {
195          var cell = cells[c];
196          // innerText works too long if we will do it every time, so we store
197          // the content of the cell
198          cell['ec:content'] = cell.innerText;
199        }
200      }
201      if (section.head || section.rows.length) {
202        this.sections.push(section);
203      }
204      this.redraw();
205    }
206    catch (err) {
207      this.dumpDebug();
208      logError(err,'Tab.initialize');
209    }
210  }, // initialize
211
212  dumpDebug : function()
213  {
214    try {
215      addDebug('tableId',this.id);
216      addDebug('numberOfRows',this.numberOfRows);
217      addDebug('navigation',this.navigation);
218      addDebug('hasHeader',this.hasHeader);
219      addDebug('hasSummary',this.hasSummary);
220      addDebug('inc',this.inc);
221      addDebug('currentColumn',this.currentColumn);
222      addDebug('page',this.page);
223      addDebug('displayRows',this.displayRows);
224      addDebug('search',this.search);
225      addDebug('drawing',this.drawing);
226      addDebug('uniqueId',this.uniqueId);
227    }
228    catch (err) {
229      logError(err,'Tab.dumpDebug');
230    }
231  }, // dumpDebug
232
233  setSort : function(sort)
234  {
235    this.sort = sort;
236  },
237
238  /**
239   * Returns a table cell that need to compare for the currentColumn according
240   * colspan in the row.
241   * @param {HTMLTableRowElement}
241 row - Table row for comparign.
242   * @returns {HTMLTableCellElement}
243   */
244  getComparableCell: function(row) {
245    var index = -1;
246    var column = this.currentColumn;
247    while (column > 0) {
248      index++;
249      column -= row.cells[index].colSpan;
250    }
251    return row.cells[index];
252  }, // getComparableCell
253
254  /**
255   * Returns the index of the find row. If there are no this row, returns -1.
256   * @param {HTMLTableRowElement} row - Row in the table that need to find index.
257   * @returns {number}
258   */
259  getRowIndex: function(row) {
260    var index = 0;
261    for (var k = 0; k < this.sections.length; ++k) {
262      var section = this.sections[k];
263      for (var l = 0; l < section.rows.length; ++l) {
264        if (section.rows[l] === row) {
265          return index;
266        }
267        index++;
268      }
269    }
270    return -1;
271  },
272
273  cellCompare: function(row1, row2)
274  {
275    try {
276      var currentColumn = this.currentColumn;
277      var sortKind = this.sort[currentColumn];
278      var prim1 = row1['ec:prim'];
279      if (!prim1) {
280        return -1;
281      }
282      var prim2 = row2['ec:prim'];
283      if (!prim2) {
284        return 1;
285      }
286
287      var key1;
288      var key2;
289      if (sortKind === 'primary') {
290        key1 = prim1;
291        key2 = prim2;
292      }
293      else {
294        var cell1 = this.getComparableCell(row1);
295        var cell2 = this.getComparableCell(row2);
296        var text1 = cell1.getAttribute('data-key') || cell1['ec:content'];
297        var text2 = cell2.getAttribute('data-key') || cell2['ec:content'];
298        switch (sortKind) {
299          case 'bool':
300            key1 = Boolean(text1);
301            key2 = Boolean(text2);
302            break;
303          case 'int':
304            key1 = parseInt(text1);
305            key1 = isNaN(key1) ? -Infinity : key1;
306            key2 = parseInt(text2);
307            key2 = isNaN(key2) ? -Infinity : key2;
308            break;
309          case 'float':
310          case 'number':
311            key1 = parseFloat(text1);
312            key1 = isNaN(key1) ? -Infinity : key1;
313            key2 = parseFloat(text2);
314            key2 = isNaN(key2) ? -Infinity : key2;
315            break;
316          case 'string':
317            key1 = text1;
318            key2 = text2;
319            break;
320          default:
321            throw new Error('Incorrect sortKind');
322            break;
323        }
324      }
325
326      if (key1 < key2) {
327        result = -1;
328      }
329      else if (key1 > key2) {
330        result = 1;
331      }
332      else if (prim1 < prim2) {
333        return -1;
334      }
335      else if (prim2 < prim1) {
336        return 1;
337      }
338      else {
339        return 0;
340      }
341
342      return this.inc ? result : -result;
343    }
344    catch (err) {
345      this.dumpDebug();
346      logError(err,'Tab.cellCompare');
347    }
348  }, // cellCompare
349
350  sortOn : function(column)
351  {
352    try {
353      if (this.drawing) {
354        return;
355      }
356      this.drawing = true;
357      this.page = 1;
358      var tab = $(this.id);
359      var rows = tab.rows;
360      var headerRowIndex = this.navigation ? 1 : 0;
361      var header = rows[headerRowIndex];
362      if (this.currentColumn == column) {
363        this.inc = ! this.inc;
364      }
365      else {
366        var lastRowIndex = rows.length - 1;
367        if (this.hasSummary) {
368          lastRowIndex--;
369        }
370        if (this.currentColumn != -1) {
371          header.cells[this.currentColumn-1].className = 'sort_both';
372          for (var i = 0; i < this.sections.length; ++i) {
373            for (var j = 0; j < this.sections[i].rows.length; ++j) {
374              removeClass(this.getComparableCell(this.sections[i].rows[j]),
375                'high');
376            }
377          }
378        }
379        this.inc = false;
380        this.currentColumn = column;
381        for (var i = 0; i < this.sections.length; ++i) {
382          for (var j = 0; j < this.sections[i].rows.length; ++j) {
383            addClass(this.getComparableCell(this.sections[i].rows[j]), 'high');
384          }
385        }
386      }
387      header.cells[column-1].className = this.inc ? 'sort_asc' : 'sort_desc';
388      var myself = this;
389      for (var i = 0; i < this.sections.length; ++i) {
390        var sectionRows = this.sections[i].rows;
391        if (sectionRows.length === 0) { // empty section
392          continue;
393        }
394        sectionRows.sort(function(cellA, cellB) {
395          return myself.cellCompare(cellA, cellB);
396        });
397        var tableSection = sectionRows[0].parentNode;
398        for (var n = sectionRows.length - 2; n >= 0; n--) {
399          var row1 = sectionRows[n];
400          var row2 = sectionRows[n + 1];
401          if (row1 && row2) { // added 23/07/2012 to prevent errors for pages incompletely loaded
402            tableSection.insertBefore(row1,row2);
403          }
404        }
405      }
406      this.redraw();
407      this.drawing = false;
408    }
409    catch (err) {
410      this.dumpDebug();
411      logError(err,'Tab.sortOn');
412    }
413  }, // sortOn
414
415  /**
416   * Show the header if it exists for all sections from array.
417   * @param {Section[]} sections - Sections that need to show the header.
418   */
419  showSectionsHeader: function(sections) {
420    for (var i = 0; i < sections.length; ++i) {
421      if (sections[i].head) {
422        removeClass(sections[i].head, 'hidden');
423      }
424    }
425  }, // showSectionsHeader
426
427  // Redraw the table depending on the current state. This function assumes
428  // the rows are already sorted and simply checks which rows should be hidden
429  redraw : function()
430  {
431    try {
432      if (!this.navigation) {
433        return;
434      }
435      // the page should display eligible entries between first and last, counting from 1
436      var displayAll = this.displayRows == 'all';
437      var last = displayAll ? this.numberOfRows : this.page * this.displayRows;
438      var first = displayAll ? 1 : last - this.displayRows + 1;
439      var eligible = 0;
440      /** Section without eligible elements.
441       * @type {Section[]}
442       */
443      var noEligibleElements = [];
444      /* Logic of showing section headers:
445         We should show all section headers from the first section that have
446         visible elements to the last section that have visible elements.
447         There may be sections between the first and the last without
448         visible/eligible rows, we also should show it.
449         Plus all section headers before the first without eligible rows.
450         Plus all section headers after the last section if this is the last
451         page. */
452      for (var i = 0; i < this.sections.length; ++i) {
453        var section = this.sections[i];
454        var hasEligibleElements = false;
455        var hasVisibleElements = false;
456        var firstEligibleElementIsVisible = false;
457        if (section.head) {
458          addClass(section.head, 'hidden');
459        }
460        for (var j = 0; j < section.rows.length; ++j) {
461          var row = section.rows[j];
462
463          if (this.search && !Tab.matchRow(row, this.search)) {
464            addClass(row, 'hidden');
465            continue;
466          }
467
468          eligible++;
469          if (eligible >= first && eligible <= last) {
470            removeClass(row, 'hidden');
471            hasVisibleElements = true;
472            if (!hasEligibleElements) {
473              firstEligibleElementIsVisible = true;
474            }
475          } else {
476            addClass(row,'hidden');
477          }
478          hasEligibleElements = true;
479        }
480        if (hasVisibleElements && section.head) {
481          removeClass(section.head, 'hidden');
482        }
483        if (hasEligibleElements) {
484          if (firstEligibleElementIsVisible) {
485            this.showSectionsHeader(noEligibleElements);
486          }
487          noEligibleElements = [];
488        } else {
489          noEligibleElements.push(section);
490        }
491      }
492      // Or displayAll is true, or it is last page.
493      if (displayAll || this.page === Math.ceil(eligible / this.displayRows)) {
494        this.showSectionsHeader(noEligibleElements);
495      }
496
497      // change the "showing n entries" part of the bar
498      $(this.id + ':rows').innerHTML = eligible;
499      $(this.id + ':from').innerHTML = eligible < first ? 0 : first;
500      $(this.id + ':to').innerHTML = eligible < first ? 0 : eligible < last ? eligible : last;
501      // change the pages bar
502      $(this.id + ':previous').style.visibility = this.page > 1 ? 'visible' : 'hidden';
503      $(this.id + ':next').style.visibility = eligible > last ? 'visible' : 'hidden';
504
505      var rightLinks = displayAll ? 0 : Math.ceil((eligible - last) / this.displayRows);
506      var leftLinks = displayAll ? 0 : ((first - 1) / this.displayRows);
507      if (leftLinks + rightLinks > 8) {
508        if (leftLinks > 4) {
509          if (rightLinks >= 4) {
510            leftLinks = 4;
511          }
512          else {
513            leftLinks = 8 - rightLinks;
514          }
515        }
516        if (rightLinks > 4) {
517          if (leftLinks >= 4) {
518            rightLinks = 4;
519          }
520          else {
521            rightLinks = 8 - leftLinks;
522          }
523        }
524      }
525      var allHidden = displayAll || (first == 1 && eligible <= last);
526      var slices = Math.ceil(this.numberOfRows / 10);
527      var fromSlice = this.page - leftLinks;
528      var toSlice = this.page + rightLinks;
529      for (var n = slices; n > 0; n--) {
530        var selected = $(this.id + ':sel' + n);
531        var pageLink = $(this.id + ':page' + n);
532        if (allHidden || n < fromSlice || n > toSlice) {
533          addClass(selected,'hidden');
534          addClass(pageLink,'hidden');
535        }
536        else if (n == this.page) {
537          removeClass(selected,'hidden');
538          addClass(pageLink,'hidden');
539        }
540        else {
541          addClass(selected,'hidden');
542          removeClass(pageLink,'hidden');
543        }
544      }
545    }
546    catch (err) {
547      this.dumpDebug();
548      logError(err,'Tab.redraw');
549    }
550  }, // redraw
551
552  // function called when the user changes the number of displayed entries
553  changeEntries : function()
554  {
555    try {
556      if (!Tab.initialized) {
557        Tab.notFullyLoaded();
558        return;
559      }
560      var field = $(this.id + ':entries');
561      if (this.drawing) {
562        EC.setSelection(field,this.displayRows);
563        return;
564      }
565      this.drawing = true;
566      this.page = 1;
567      this.displayRows = EC.getSelection(field);
568      this.redraw();
569      this.drawing = false;
570    }
571    catch (err) {
572      this.dumpDebug();
573      logError(err,'Tab.changeEntries');
574    }
575  }, // changeEntries
576
577  // function called when the user clicks on "next page"
578  nextPage : function()
579  {
580    try {
581      return this.setPage(this.page+1);
582    }
583    catch (err) {
584      logError(err,'Tab.nextPage');
585    }
586  }, // nextPage
587
588  // function called when the user clicks on "previous page"
589  previousPage : function()
590  {
591    try {
592      return this.setPage(this.page-1);
593    }
594    catch (err) {
595      logError(err,'Tab.previousPage');
596    }
597  }, // previousPage
598
599  // function called when the user clicks on a page number
600  // page numbers start from 1
601  setPage : function(page)
602  {
603    try {
604      if (!Tab.initialized) {
605        Tab.notFullyLoaded();
606        return;
607      }
608      if (this.drawing) {
609        return false;
610      }
611      this.drawing = true;
612      this.page = page;
613      this.redraw();
614      this.drawing = false;
615      return false;
616    }
617    catch (err) {
618      this.dumpDebug();
619      logError(err,'Tab.setPage');
620    }
621  }, // setPage
622
623  searchUpdate : function()
624  {
625    try {
626      if (!Tab.initialized) {
627        Tab.notFullyLoaded();
628        return;
629      }
630      var searchString = $(this.id + ':search').value;
631      if (searchString == this.search) {
632        return;
633      }
634      if (this.drawing) {
635        // there should be rescheduling of this function call in future
636        return;
637      }
638      this.search = searchString;
639      this.drawing = true;
640      this.page = 1;
641      this.redraw();
642      this.drawing = false;
643    }
644    catch (err) {
645      this.dumpDebug();
646      logError(err,'Tab.searchUpdate');
647    }
648  }, // searchUpdate
649
650  // save state in the session storage
651  saveState : function()
652  {
653    try {
654      saveObject(this.uniqueId,
655                 {i : this.inc,c : this.currentColumn,p : this.page,
656                  d : this.displayRows,s : this.search});
657    }
658    catch (err) {
659      this.dumpDebug();
660      logError(err,'Tab.saveState');
661    }
662  },
663
664  // restore state from the session storage
665  restoreState : function()
666  {
667    "use strict";
668    try {
669      var uniqueId = this.uniqueId;
670      var state = getObject(uniqueId);
671      if (!state) {
672        return;
673      }
674      this.displayRows = state.d;
675      if (this.navigation) {
676        EC.setSelection($(this.id + ':entries'),state.d);
677        this.search = state.s;
678        $(this.id + ':search').value = state.s;
679      }
680      if (state.c != -1) {
681        this.sortOn(state.c);
682        // if the order is reverse, it should be sorted twice
683        if (state.i) {
684          this.sortOn(state.c);
685        }
686      }
687      this.page = state.p;
688      this.redraw();
689      deleteObject(this.uniqueId);
690    }
691    catch (err) {
692      this.dumpDebug();
693      logError(err,'Tab.restoreState');
694    }
695  }
696}; // Tab.prototype
697
698Tab.matchRow = function(row,searchString)
699{
700  try {
701    searchString = searchString.toLowerCase();
702    var cells = row.cells;
703    for (var c = cells.length - 1; c >= 0; c--) {
704      if (cells[c]['ec:content'].toLowerCase().trim().indexOf(searchString) !== -1) {
705        return true;
706      }
707    }
708    return false;
709  }
710  catch (err) {
711    logError(err,'Tab.matchRow');
712  }
713} // Tab.matchRow
714
715Tab.hashChanged = function(event)
716{
717  try {
718    Tab.hashChanged2(event.newURL);
719  }
720  catch (err) {
721    logError(err,'Tab.hashChanged');
722  }
723} // Tab.hashChanged
724
725Tab.hashChanged2 = function(URL)
726{
727  try {
728    var hashIndex = URL.indexOf('#');
729    if (hashIndex == -1) {
730      return;
731    }
732    var name = URL.substring(hashIndex + 1);
733    if (name == '') {
734      return;
735    }
736
737    // find if the element with this name is contained in a table row
738    var elements = document.getElementsByName(name);
739    for (var i = 0; i < elements.length; ++i) {
740      var tableRow = elements[i];
741      while (tableRow && tableRow.tagName != 'TR') {
742        tableRow = tableRow.parentNode;
743      }
744      if (!tableRow) {
745        continue;
746      }
747      // tableRow is a <tr> element
748      // find a <table> element containing this row
749      var table = tableRow.parentNode;
750      while (table.tagName != 'TABLE') {
751        table = table.parentNode;
752      }
753      var tab = Tab.tables[table.id];
754      if (!tab) { // table element but not one of our tables
755        continue;
756      }
757      if (!tab.hashScroll) {
758        // tab found but we should not scroll to it
759        return;
760      }
761
762      if (tab.search) {
763        $(tab.id + ':search').value = '';
764        tab.search = '';
765      }
766
767      var page = 1;
768      var rowsCount = parseInt(tab.displayRows);
769      if (rowsCount) {
770        var index = tab.getRowIndex(tableRow);
771        page = Math.floor(index / rowsCount) + 1;
772      }
773      tab.setPage(page);
774      tableRow.scrollIntoView();
775      return;
776    }
777  }
778  catch (err) {
779    logError(err,'Tab.hashChanged2');
780  }
781} // Tab.hashChanged2
782
783addOnLoad(Tab.init);
784addOnBeforeUnload(Tab.saveAll);

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.