1
0

pagedtable.js 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  1. // Production steps of ECMA-262, Edition 5, 15.4.4.18
  2. // Reference: http://es5.github.io/#x15.4.4.18
  3. if (!Array.prototype.forEach) {
  4. Array.prototype.forEach = function(callback, thisArg) {
  5. var T, k;
  6. if (this === null) {
  7. throw new TypeError(' this is null or not defined');
  8. }
  9. // 1. Let O be the result of calling toObject() passing the
  10. // |this| value as the argument.
  11. var O = Object(this);
  12. // 2. Let lenValue be the result of calling the Get() internal
  13. // method of O with the argument "length".
  14. // 3. Let len be toUint32(lenValue).
  15. var len = O.length >>> 0;
  16. // 4. If isCallable(callback) is false, throw a TypeError exception.
  17. // See: http://es5.github.com/#x9.11
  18. if (typeof callback !== "function") {
  19. throw new TypeError(callback + ' is not a function');
  20. }
  21. // 5. If thisArg was supplied, let T be thisArg; else let
  22. // T be undefined.
  23. if (arguments.length > 1) {
  24. T = thisArg;
  25. }
  26. // 6. Let k be 0
  27. k = 0;
  28. // 7. Repeat, while k < len
  29. while (k < len) {
  30. var kValue;
  31. // a. Let Pk be ToString(k).
  32. // This is implicit for LHS operands of the in operator
  33. // b. Let kPresent be the result of calling the HasProperty
  34. // internal method of O with argument Pk.
  35. // This step can be combined with c
  36. // c. If kPresent is true, then
  37. if (k in O) {
  38. // i. Let kValue be the result of calling the Get internal
  39. // method of O with argument Pk.
  40. kValue = O[k];
  41. // ii. Call the Call internal method of callback with T as
  42. // the this value and argument list containing kValue, k, and O.
  43. callback.call(T, kValue, k, O);
  44. }
  45. // d. Increase k by 1.
  46. k++;
  47. }
  48. // 8. return undefined
  49. };
  50. }
  51. // Production steps of ECMA-262, Edition 5, 15.4.4.19
  52. // Reference: http://es5.github.io/#x15.4.4.19
  53. if (!Array.prototype.map) {
  54. Array.prototype.map = function(callback, thisArg) {
  55. var T, A, k;
  56. if (this == null) {
  57. throw new TypeError(' this is null or not defined');
  58. }
  59. // 1. Let O be the result of calling ToObject passing the |this|
  60. // value as the argument.
  61. var O = Object(this);
  62. // 2. Let lenValue be the result of calling the Get internal
  63. // method of O with the argument "length".
  64. // 3. Let len be ToUint32(lenValue).
  65. var len = O.length >>> 0;
  66. // 4. If IsCallable(callback) is false, throw a TypeError exception.
  67. // See: http://es5.github.com/#x9.11
  68. if (typeof callback !== 'function') {
  69. throw new TypeError(callback + ' is not a function');
  70. }
  71. // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
  72. if (arguments.length > 1) {
  73. T = thisArg;
  74. }
  75. // 6. Let A be a new array created as if by the expression new Array(len)
  76. // where Array is the standard built-in constructor with that name and
  77. // len is the value of len.
  78. A = new Array(len);
  79. // 7. Let k be 0
  80. k = 0;
  81. // 8. Repeat, while k < len
  82. while (k < len) {
  83. var kValue, mappedValue;
  84. // a. Let Pk be ToString(k).
  85. // This is implicit for LHS operands of the in operator
  86. // b. Let kPresent be the result of calling the HasProperty internal
  87. // method of O with argument Pk.
  88. // This step can be combined with c
  89. // c. If kPresent is true, then
  90. if (k in O) {
  91. // i. Let kValue be the result of calling the Get internal
  92. // method of O with argument Pk.
  93. kValue = O[k];
  94. // ii. Let mappedValue be the result of calling the Call internal
  95. // method of callback with T as the this value and argument
  96. // list containing kValue, k, and O.
  97. mappedValue = callback.call(T, kValue, k, O);
  98. // iii. Call the DefineOwnProperty internal method of A with arguments
  99. // Pk, Property Descriptor
  100. // { Value: mappedValue,
  101. // Writable: true,
  102. // Enumerable: true,
  103. // Configurable: true },
  104. // and false.
  105. // In browsers that support Object.defineProperty, use the following:
  106. // Object.defineProperty(A, k, {
  107. // value: mappedValue,
  108. // writable: true,
  109. // enumerable: true,
  110. // configurable: true
  111. // });
  112. // For best browser support, use the following:
  113. A[k] = mappedValue;
  114. }
  115. // d. Increase k by 1.
  116. k++;
  117. }
  118. // 9. return A
  119. return A;
  120. };
  121. }
  122. var PagedTable = function (pagedTable) {
  123. var me = this;
  124. var source = function(pagedTable) {
  125. var sourceElems = [].slice.call(pagedTable.children).filter(function(e) {
  126. return e.hasAttribute("data-pagedtable-source");
  127. });
  128. if (sourceElems === null || sourceElems.length !== 1) {
  129. throw("A single data-pagedtable-source was not found");
  130. }
  131. return JSON.parse(sourceElems[0].innerHTML);
  132. }(pagedTable);
  133. var options = function(source) {
  134. var options = typeof(source.options) !== "undefined" &&
  135. source.options !== null ? source.options : {};
  136. var columns = typeof(options.columns) !== "undefined" ? options.columns : {};
  137. var rows = typeof(options.rows) !== "undefined" ? options.rows : {};
  138. var positiveIntOrNull = function(value) {
  139. return parseInt(value) >= 0 ? parseInt(value) : null;
  140. };
  141. return {
  142. pages: positiveIntOrNull(options.pages),
  143. rows: {
  144. min: positiveIntOrNull(rows.min),
  145. max: positiveIntOrNull(rows.max),
  146. total: positiveIntOrNull(rows.total)
  147. },
  148. columns: {
  149. min: positiveIntOrNull(columns.min),
  150. max: positiveIntOrNull(columns.max),
  151. total: positiveIntOrNull(columns.total)
  152. }
  153. };
  154. }(source);
  155. var Measurer = function() {
  156. // set some default initial values that will get adjusted in runtime
  157. me.measures = {
  158. padding: 12,
  159. character: 8,
  160. height: 15,
  161. defaults: true
  162. };
  163. me.calculate = function(measuresCell) {
  164. if (!me.measures.defaults)
  165. return;
  166. var measuresCellStyle = window.getComputedStyle(measuresCell, null);
  167. var newPadding = parsePadding(measuresCellStyle.paddingLeft) +
  168. parsePadding(measuresCellStyle.paddingRight);
  169. var sampleString = "ABCDEFGHIJ0123456789";
  170. var newCharacter = Math.ceil(measuresCell.clientWidth / sampleString.length);
  171. if (newPadding <= 0 || newCharacter <= 0)
  172. return;
  173. me.measures.padding = newPadding;
  174. me.measures.character = newCharacter;
  175. me.measures.height = measuresCell.clientHeight;
  176. me.measures.defaults = false;
  177. };
  178. return me;
  179. };
  180. var Page = function(data, options) {
  181. var me = this;
  182. var defaults = {
  183. max: 7,
  184. rows: 10
  185. };
  186. var totalPages = function() {
  187. return Math.ceil(data.length / me.rows);
  188. };
  189. me.number = 0;
  190. me.max = options.pages !== null ? options.pages : defaults.max;
  191. me.visible = me.max;
  192. me.rows = options.rows.min !== null ? options.rows.min : defaults.rows;
  193. me.total = totalPages();
  194. me.setRows = function(newRows) {
  195. me.rows = newRows;
  196. me.total = totalPages();
  197. };
  198. me.setPageNumber = function(newPageNumber) {
  199. if (newPageNumber < 0) newPageNumber = 0;
  200. if (newPageNumber >= me.total) newPageNumber = me.total - 1;
  201. me.number = newPageNumber;
  202. };
  203. me.setVisiblePages = function(visiblePages) {
  204. me.visible = Math.min(me.max, visiblePages);
  205. me.setPageNumber(me.number);
  206. };
  207. me.getVisiblePageRange = function() {
  208. var start = me.number - Math.max(Math.floor((me.visible - 1) / 2), 0);
  209. var end = me.number + Math.floor(me.visible / 2) + 1;
  210. var pageCount = me.total;
  211. if (start < 0) {
  212. var diffToStart = 0 - start;
  213. start += diffToStart;
  214. end += diffToStart;
  215. }
  216. if (end > pageCount) {
  217. var diffToEnd = end - pageCount;
  218. start -= diffToEnd;
  219. end -= diffToEnd;
  220. }
  221. start = start < 0 ? 0 : start;
  222. end = end >= pageCount ? pageCount : end;
  223. var first = false;
  224. var last = false;
  225. if (start > 0 && me.visible > 1) {
  226. start = start + 1;
  227. first = true;
  228. }
  229. if (end < pageCount && me.visible > 2) {
  230. end = end - 1;
  231. last = true;
  232. }
  233. return {
  234. first: first,
  235. start: start,
  236. end: end,
  237. last: last
  238. };
  239. };
  240. me.getRowStart = function() {
  241. var rowStart = page.number * page.rows;
  242. if (rowStart < 0)
  243. rowStart = 0;
  244. return rowStart;
  245. };
  246. me.getRowEnd = function() {
  247. var rowStart = me.getRowStart();
  248. return Math.min(rowStart + me.rows, data.length);
  249. };
  250. me.getPaddingRows = function() {
  251. var rowStart = me.getRowStart();
  252. var rowEnd = me.getRowEnd();
  253. return data.length > me.rows ? me.rows - (rowEnd - rowStart) : 0;
  254. };
  255. };
  256. var Columns = function(data, columns, options) {
  257. var me = this;
  258. me.defaults = {
  259. min: 5
  260. };
  261. me.number = 0;
  262. me.visible = 0;
  263. me.total = columns.length;
  264. me.subset = [];
  265. me.padding = 0;
  266. me.min = options.columns.min !== null ? options.columns.min : me.defaults.min;
  267. me.max = options.columns.max !== null ? options.columns.max : null;
  268. me.widths = {};
  269. var widthsLookAhead = Math.max(100, options.rows.min);
  270. var paddingColChars = 10;
  271. me.emptyNames = function() {
  272. columns.forEach(function(column) {
  273. if (columns.label !== null && columns.label !== "")
  274. return false;
  275. });
  276. return true;
  277. };
  278. var parsePadding = function(value) {
  279. return parseInt(value) >= 0 ? parseInt(value) : 0;
  280. };
  281. me.calculateWidths = function(measures) {
  282. columns.forEach(function(column) {
  283. var maxChars = Math.max(
  284. column.label.toString().length,
  285. column.type.toString().length
  286. );
  287. for (var idxRow = 0; idxRow < Math.min(widthsLookAhead, data.length); idxRow++) {
  288. maxChars = Math.max(maxChars, data[idxRow][column.name.toString()].length);
  289. }
  290. me.widths[column.name] = {
  291. // width in characters
  292. chars: maxChars,
  293. // width for the inner html columns
  294. inner: maxChars * measures.character,
  295. // width adding outer styles like padding
  296. outer: maxChars * measures.character + measures.padding
  297. };
  298. });
  299. };
  300. me.getWidth = function() {
  301. var widthOuter = 0;
  302. for (var idxCol = 0; idxCol < me.subset.length; idxCol++) {
  303. var columnName = me.subset[idxCol].name;
  304. widthOuter = widthOuter + me.widths[columnName].outer;
  305. }
  306. widthOuter = widthOuter + me.padding * paddingColChars * measurer.measures.character;
  307. if (me.hasMoreLeftColumns()) {
  308. widthOuter = widthOuter + columnNavigationWidthPX + measurer.measures.padding;
  309. }
  310. if (me.hasMoreRightColumns()) {
  311. widthOuter = widthOuter + columnNavigationWidthPX + measurer.measures.padding;
  312. }
  313. return widthOuter;
  314. };
  315. me.updateSlice = function() {
  316. if (me.number + me.visible >= me.total)
  317. me.number = me.total - me.visible;
  318. if (me.number < 0) me.number = 0;
  319. me.subset = columns.slice(me.number, Math.min(me.number + me.visible, me.total));
  320. me.subset = me.subset.map(function(column) {
  321. Object.keys(column).forEach(function(colKey) {
  322. column[colKey] = column[colKey] === null ? "" : column[colKey].toString();
  323. });
  324. column.width = null;
  325. return column;
  326. });
  327. };
  328. me.setVisibleColumns = function(columnNumber, newVisibleColumns, paddingCount) {
  329. me.number = columnNumber;
  330. me.visible = newVisibleColumns;
  331. me.padding = paddingCount;
  332. me.updateSlice();
  333. };
  334. me.incColumnNumber = function(increment) {
  335. me.number = me.number + increment;
  336. };
  337. me.setColumnNumber = function(newNumber) {
  338. me.number = newNumber;
  339. };
  340. me.setPaddingCount = function(newPadding) {
  341. me.padding = newPadding;
  342. };
  343. me.getPaddingCount = function() {
  344. return me.padding;
  345. };
  346. me.hasMoreLeftColumns = function() {
  347. return me.number > 0;
  348. };
  349. me.hasMoreRightColumns = function() {
  350. return me.number + me.visible < me.total;
  351. };
  352. me.updateSlice(0);
  353. return me;
  354. };
  355. var data = source.data;
  356. var page = new Page(data, options);
  357. var measurer = new Measurer(data, options);
  358. var columns = new Columns(data, source.columns, options);
  359. var table = null;
  360. var tableDiv = null;
  361. var header = null;
  362. var footer = null;
  363. var tbody = null;
  364. // Caches pagedTable.clientWidth, specially for webkit
  365. var cachedPagedTableClientWidth = null;
  366. var onChangeCallbacks = [];
  367. var clearSelection = function() {
  368. if(document.selection && document.selection.empty) {
  369. document.selection.empty();
  370. } else if(window.getSelection) {
  371. var sel = window.getSelection();
  372. sel.removeAllRanges();
  373. }
  374. };
  375. var columnNavigationWidthPX = 5;
  376. var renderColumnNavigation = function(increment, backwards) {
  377. var arrow = document.createElement("div");
  378. arrow.setAttribute("style",
  379. "border-top: " + columnNavigationWidthPX + "px solid transparent;" +
  380. "border-bottom: " + columnNavigationWidthPX + "px solid transparent;" +
  381. "border-" + (backwards ? "right" : "left") + ": " + columnNavigationWidthPX + "px solid;");
  382. var header = document.createElement("th");
  383. header.appendChild(arrow);
  384. header.setAttribute("style",
  385. "cursor: pointer;" +
  386. "vertical-align: middle;" +
  387. "min-width: " + columnNavigationWidthPX + "px;" +
  388. "width: " + columnNavigationWidthPX + "px;");
  389. header.onclick = function() {
  390. columns.incColumnNumber(backwards ? -1 : increment);
  391. me.animateColumns(backwards);
  392. renderFooter();
  393. clearSelection();
  394. triggerOnChange();
  395. };
  396. return header;
  397. };
  398. var maxColumnWidth = function(width) {
  399. var padding = 80;
  400. var columnMax = Math.max(cachedPagedTableClientWidth - padding, 0);
  401. return parseInt(width) > 0 ?
  402. Math.min(columnMax, parseInt(width)) + "px" :
  403. columnMax + "px";
  404. };
  405. var clearHeader = function() {
  406. var thead = pagedTable.querySelectorAll("thead")[0];
  407. thead.innerHTML = "";
  408. };
  409. var renderHeader = function(clear) {
  410. cachedPagedTableClientWidth = pagedTable.clientWidth;
  411. var fragment = document.createDocumentFragment();
  412. header = document.createElement("tr");
  413. fragment.appendChild(header);
  414. if (columns.number > 0)
  415. header.appendChild(renderColumnNavigation(-columns.visible, true));
  416. columns.subset = columns.subset.map(function(columnData) {
  417. var column = document.createElement("th");
  418. column.setAttribute("align", columnData.align);
  419. column.style.textAlign = columnData.align;
  420. column.style.maxWidth = maxColumnWidth(null);
  421. if (columnData.width) {
  422. column.style.minWidth =
  423. column.style.maxWidth = maxColumnWidth(columnData.width);
  424. }
  425. var columnName = document.createElement("div");
  426. columnName.setAttribute("class", "pagedtable-header-name");
  427. if (columnData.label === "") {
  428. columnName.innerHTML = "&nbsp;";
  429. }
  430. else {
  431. columnName.appendChild(document.createTextNode(columnData.label));
  432. }
  433. column.appendChild(columnName);
  434. var columnType = document.createElement("div");
  435. columnType.setAttribute("class", "pagedtable-header-type");
  436. if (columnData.type === "") {
  437. columnType.innerHTML = "&nbsp;";
  438. }
  439. else {
  440. columnType.appendChild(document.createTextNode("<" + columnData.type + ">"));
  441. }
  442. column.appendChild(columnType);
  443. header.appendChild(column);
  444. columnData.element = column;
  445. return columnData;
  446. });
  447. for (var idx = 0; idx < columns.getPaddingCount(); idx++) {
  448. var paddingCol = document.createElement("th");
  449. paddingCol.setAttribute("class", "pagedtable-padding-col");
  450. header.appendChild(paddingCol);
  451. }
  452. if (columns.number + columns.visible < columns.total)
  453. header.appendChild(renderColumnNavigation(columns.visible, false));
  454. if (typeof(clear) == "undefined" || clear) clearHeader();
  455. var thead = pagedTable.querySelectorAll("thead")[0];
  456. thead.appendChild(fragment);
  457. };
  458. me.animateColumns = function(backwards) {
  459. var thead = pagedTable.querySelectorAll("thead")[0];
  460. var headerOld = thead.querySelectorAll("tr")[0];
  461. var tbodyOld = table.querySelectorAll("tbody")[0];
  462. me.fitColumns(backwards);
  463. renderHeader(false);
  464. header.style.opacity = "0";
  465. header.style.transform = backwards ? "translateX(-30px)" : "translateX(30px)";
  466. header.style.transition = "transform 200ms linear, opacity 200ms";
  467. header.style.transitionDelay = "0";
  468. renderBody(false);
  469. if (headerOld) {
  470. headerOld.style.position = "absolute";
  471. headerOld.style.transform = "translateX(0px)";
  472. headerOld.style.opacity = "1";
  473. headerOld.style.transition = "transform 100ms linear, opacity 100ms";
  474. headerOld.setAttribute("class", "pagedtable-remove-head");
  475. if (headerOld.style.transitionEnd) {
  476. headerOld.addEventListener("transitionend", function() {
  477. var headerOldByClass = thead.querySelector(".pagedtable-remove-head");
  478. if (headerOldByClass) thead.removeChild(headerOldByClass);
  479. });
  480. }
  481. else {
  482. thead.removeChild(headerOld);
  483. }
  484. }
  485. if (tbodyOld) table.removeChild(tbodyOld);
  486. tbody.style.opacity = "0";
  487. tbody.style.transition = "transform 200ms linear, opacity 200ms";
  488. tbody.style.transitionDelay = "0ms";
  489. // force relayout
  490. window.getComputedStyle(header).opacity;
  491. window.getComputedStyle(tbody).opacity;
  492. if (headerOld) {
  493. headerOld.style.transform = backwards ? "translateX(20px)" : "translateX(-30px)";
  494. headerOld.style.opacity = "0";
  495. }
  496. header.style.transform = "translateX(0px)";
  497. header.style.opacity = "1";
  498. tbody.style.opacity = "1";
  499. }
  500. me.onChange = function(callback) {
  501. onChangeCallbacks.push(callback);
  502. };
  503. var triggerOnChange = function() {
  504. onChangeCallbacks.forEach(function(onChange) {
  505. onChange();
  506. });
  507. };
  508. var clearBody = function() {
  509. if (tbody) {
  510. table.removeChild(tbody);
  511. tbody = null;
  512. }
  513. };
  514. var renderBody = function(clear) {
  515. cachedPagedTableClientWidth = pagedTable.clientWidth
  516. var fragment = document.createDocumentFragment();
  517. var pageData = data.slice(page.getRowStart(), page.getRowEnd());
  518. pageData.forEach(function(dataRow, idxRow) {
  519. var htmlRow = document.createElement("tr");
  520. htmlRow.setAttribute("class", (idxRow % 2 !==0) ? "even" : "odd");
  521. if (columns.hasMoreLeftColumns())
  522. htmlRow.appendChild(document.createElement("td"));
  523. columns.subset.forEach(function(columnData) {
  524. var cellName = columnData.name;
  525. var dataCell = dataRow[cellName];
  526. var htmlCell = document.createElement("td");
  527. if (dataCell === "NA") htmlCell.setAttribute("class", "pagedtable-na-cell");
  528. if (dataCell === "__NA__") dataCell = "NA";
  529. var cellText = document.createTextNode(dataCell);
  530. htmlCell.appendChild(cellText);
  531. if (dataCell.length > 50) {
  532. htmlCell.setAttribute("title", dataCell);
  533. }
  534. htmlCell.setAttribute("align", columnData.align);
  535. htmlCell.style.textAlign = columnData.align;
  536. htmlCell.style.maxWidth = maxColumnWidth(null);
  537. if (columnData.width) {
  538. htmlCell.style.minWidth = htmlCell.style.maxWidth = maxColumnWidth(columnData.width);
  539. }
  540. htmlRow.appendChild(htmlCell);
  541. });
  542. for (var idx = 0; idx < columns.getPaddingCount(); idx++) {
  543. var paddingCol = document.createElement("td");
  544. paddingCol.setAttribute("class", "pagedtable-padding-col");
  545. htmlRow.appendChild(paddingCol);
  546. }
  547. if (columns.hasMoreRightColumns())
  548. htmlRow.appendChild(document.createElement("td"));
  549. fragment.appendChild(htmlRow);
  550. });
  551. for (var idxPadding = 0; idxPadding < page.getPaddingRows(); idxPadding++) {
  552. var paddingRow = document.createElement("tr");
  553. var paddingCellRow = document.createElement("td");
  554. paddingCellRow.innerHTML = "&nbsp;";
  555. paddingCellRow.setAttribute("colspan", "100%");
  556. paddingRow.appendChild(paddingCellRow);
  557. fragment.appendChild(paddingRow);
  558. }
  559. if (typeof(clear) == "undefined" || clear) clearBody();
  560. tbody = document.createElement("tbody");
  561. tbody.appendChild(fragment);
  562. table.appendChild(tbody);
  563. };
  564. var getLabelInfo = function() {
  565. var pageStart = page.getRowStart();
  566. var pageEnd = page.getRowEnd();
  567. var totalRows = data.length;
  568. var totalRowsLabel = options.rows.total ? options.rows.total : totalRows;
  569. var totalRowsLabelFormat = totalRowsLabel.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,');
  570. var infoText = (pageStart + 1) + "-" + pageEnd + " of " + totalRowsLabelFormat + " rows";
  571. if (totalRows < page.rows) {
  572. infoText = totalRowsLabel + " row" + (totalRows != 1 ? "s" : "");
  573. }
  574. if (columns.total > columns.visible) {
  575. var totalColumnsLabel = options.columns.total ? options.columns.total : columns.total;
  576. infoText = infoText + " | " + (columns.number + 1) + "-" +
  577. (Math.min(columns.number + columns.visible, columns.total)) +
  578. " of " + totalColumnsLabel + " columns";
  579. }
  580. return infoText;
  581. };
  582. var clearFooter = function() {
  583. footer = pagedTable.querySelectorAll("div.pagedtable-footer")[0];
  584. footer.innerHTML = "";
  585. return footer;
  586. };
  587. var createPageLink = function(idxPage) {
  588. var pageLink = document.createElement("a");
  589. pageLinkClass = idxPage === page.number ? "pagedtable-index pagedtable-index-current" : "pagedtable-index";
  590. pageLink.setAttribute("class", pageLinkClass);
  591. pageLink.setAttribute("data-page-index", idxPage);
  592. pageLink.onclick = function() {
  593. page.setPageNumber(parseInt(this.getAttribute("data-page-index")));
  594. renderBody();
  595. renderFooter();
  596. triggerOnChange();
  597. };
  598. pageLink.appendChild(document.createTextNode(idxPage + 1));
  599. return pageLink;
  600. }
  601. var renderFooter = function() {
  602. footer = clearFooter();
  603. var next = document.createElement("a");
  604. next.appendChild(document.createTextNode("Next"));
  605. next.onclick = function() {
  606. page.setPageNumber(page.number + 1);
  607. renderBody();
  608. renderFooter();
  609. triggerOnChange();
  610. };
  611. if (data.length > page.rows) footer.appendChild(next);
  612. var pageNumbers = document.createElement("div");
  613. pageNumbers.setAttribute("class", "pagedtable-indexes");
  614. var pageRange = page.getVisiblePageRange();
  615. if (pageRange.first) {
  616. var pageLink = createPageLink(0);
  617. pageNumbers.appendChild(pageLink);
  618. var pageSeparator = document.createElement("div");
  619. pageSeparator.setAttribute("class", "pagedtable-index-separator-left");
  620. pageSeparator.appendChild(document.createTextNode("..."))
  621. pageNumbers.appendChild(pageSeparator);
  622. }
  623. for (var idxPage = pageRange.start; idxPage < pageRange.end; idxPage++) {
  624. var pageLink = createPageLink(idxPage);
  625. pageNumbers.appendChild(pageLink);
  626. }
  627. if (pageRange.last) {
  628. var pageSeparator = document.createElement("div");
  629. pageSeparator.setAttribute("class", "pagedtable-index-separator-right");
  630. pageSeparator.appendChild(document.createTextNode("..."))
  631. pageNumbers.appendChild(pageSeparator);
  632. var pageLink = createPageLink(page.total - 1);
  633. pageNumbers.appendChild(pageLink);
  634. }
  635. if (data.length > page.rows) footer.appendChild(pageNumbers);
  636. var previous = document.createElement("a");
  637. previous.appendChild(document.createTextNode("Previous"));
  638. previous.onclick = function() {
  639. page.setPageNumber(page.number - 1);
  640. renderBody();
  641. renderFooter();
  642. triggerOnChange();
  643. };
  644. if (data.length > page.rows) footer.appendChild(previous);
  645. var infoLabel = document.createElement("div");
  646. infoLabel.setAttribute("class", "pagedtable-info");
  647. infoLabel.setAttribute("title", getLabelInfo());
  648. infoLabel.appendChild(document.createTextNode(getLabelInfo()));
  649. footer.appendChild(infoLabel);
  650. var enabledClass = "pagedtable-index-nav";
  651. var disabledClass = "pagedtable-index-nav pagedtable-index-nav-disabled";
  652. previous.setAttribute("class", page.number <= 0 ? disabledClass : enabledClass);
  653. next.setAttribute("class", (page.number + 1) * page.rows >= data.length ? disabledClass : enabledClass);
  654. };
  655. var measuresCell = null;
  656. var renderMeasures = function() {
  657. var measuresTable = document.createElement("table");
  658. measuresTable.style.visibility = "hidden";
  659. measuresTable.style.position = "absolute";
  660. measuresTable.style.whiteSpace = "nowrap";
  661. measuresTable.style.height = "auto";
  662. measuresTable.style.width = "auto";
  663. var measuresRow = document.createElement("tr");
  664. measuresTable.appendChild(measuresRow);
  665. measuresCell = document.createElement("td");
  666. var sampleString = "ABCDEFGHIJ0123456789";
  667. measuresCell.appendChild(document.createTextNode(sampleString));
  668. measuresRow.appendChild(measuresCell);
  669. tableDiv.appendChild(measuresTable);
  670. }
  671. me.init = function() {
  672. tableDiv = document.createElement("div");
  673. pagedTable.appendChild(tableDiv);
  674. var pagedTableClass = data.length > 0 ?
  675. "pagedtable pagedtable-not-empty" :
  676. "pagedtable pagedtable-empty";
  677. if (columns.total == 0 || (columns.emptyNames() && data.length == 0)) {
  678. pagedTableClass = pagedTableClass + " pagedtable-empty-columns";
  679. }
  680. tableDiv.setAttribute("class", pagedTableClass);
  681. renderMeasures();
  682. measurer.calculate(measuresCell);
  683. columns.calculateWidths(measurer.measures);
  684. table = document.createElement("table");
  685. table.setAttribute("cellspacing", "0");
  686. table.setAttribute("class", "table table-condensed");
  687. tableDiv.appendChild(table);
  688. table.appendChild(document.createElement("thead"));
  689. var footerDiv = document.createElement("div");
  690. footerDiv.setAttribute("class", "pagedtable-footer");
  691. tableDiv.appendChild(footerDiv);
  692. // if the host has not yet provided horizontal space, render hidden
  693. if (tableDiv.clientWidth <= 0) {
  694. tableDiv.style.opacity = "0";
  695. }
  696. me.render();
  697. // retry seizing columns later if the host has not provided space
  698. function retryFit() {
  699. if (tableDiv.clientWidth <= 0) {
  700. setTimeout(retryFit, 100);
  701. } else {
  702. me.render();
  703. triggerOnChange();
  704. }
  705. }
  706. if (tableDiv.clientWidth <= 0) {
  707. retryFit();
  708. }
  709. };
  710. var registerWidths = function() {
  711. columns.subset = columns.subset.map(function(column) {
  712. column.width = columns.widths[column.name].inner;
  713. return column;
  714. });
  715. };
  716. var parsePadding = function(value) {
  717. return parseInt(value) >= 0 ? parseInt(value) : 0;
  718. };
  719. me.fixedHeight = function() {
  720. return options.rows.max != null;
  721. }
  722. me.fitRows = function() {
  723. if (me.fixedHeight())
  724. return;
  725. measurer.calculate(measuresCell);
  726. var rows = options.rows.min !== null ? options.rows.min : 0;
  727. var headerHeight = header !== null && header.offsetHeight > 0 ? header.offsetHeight : 0;
  728. var footerHeight = footer !== null && footer.offsetHeight > 0 ? footer.offsetHeight : 0;
  729. if (pagedTable.offsetHeight > 0) {
  730. var availableHeight = pagedTable.offsetHeight - headerHeight - footerHeight;
  731. rows = Math.floor((availableHeight) / measurer.measures.height);
  732. }
  733. rows = options.rows.min !== null ? Math.max(options.rows.min, rows) : rows;
  734. page.setRows(rows);
  735. }
  736. // The goal of this function is to add as many columns as possible
  737. // starting from left-to-right, when the right most limit is reached
  738. // it tries to add columns from the left as well.
  739. //
  740. // When startBackwards is true columns are added from right-to-left
  741. me.fitColumns = function(startBackwards) {
  742. measurer.calculate(measuresCell);
  743. columns.calculateWidths(measurer.measures);
  744. if (tableDiv.clientWidth > 0) {
  745. tableDiv.style.opacity = 1;
  746. }
  747. var visibleColumns = tableDiv.clientWidth <= 0 ? Math.max(columns.min, 1) : 1;
  748. var columnNumber = columns.number;
  749. var paddingCount = 0;
  750. // track a list of added columns as we build the visible ones to allow us
  751. // to remove columns when they don't fit anymore.
  752. var columnHistory = [];
  753. var lastTableHeight = 0;
  754. var backwards = startBackwards;
  755. var tableDivStyle = window.getComputedStyle(tableDiv, null);
  756. var tableDivPadding = parsePadding(tableDivStyle.paddingLeft) +
  757. parsePadding(tableDivStyle.paddingRight);
  758. var addPaddingCol = false;
  759. var currentWidth = 0;
  760. while (true) {
  761. columns.setVisibleColumns(columnNumber, visibleColumns, paddingCount);
  762. currentWidth = columns.getWidth();
  763. if (tableDiv.clientWidth - tableDivPadding < currentWidth) {
  764. break;
  765. }
  766. columnHistory.push({
  767. columnNumber: columnNumber,
  768. visibleColumns: visibleColumns,
  769. paddingCount: paddingCount
  770. });
  771. if (columnHistory.length > 100) {
  772. console.error("More than 100 tries to fit columns, aborting");
  773. break;
  774. }
  775. if (columns.max !== null &&
  776. columns.visible + columns.getPaddingCount() >= columns.max) {
  777. break;
  778. }
  779. // if we run out of right-columns
  780. if (!backwards && columnNumber + columns.visible >= columns.total) {
  781. // if we started adding right-columns, try adding left-columns
  782. if (!startBackwards && columnNumber > 0) {
  783. backwards = true;
  784. }
  785. else if (columns.min === null || visibleColumns + columns.getPaddingCount() >= columns.min) {
  786. break;
  787. }
  788. else {
  789. paddingCount = paddingCount + 1;
  790. }
  791. }
  792. // if we run out of left-columns
  793. if (backwards && columnNumber == 0) {
  794. // if we started adding left-columns, try adding right-columns
  795. if (startBackwards && columnNumber + columns.visible < columns.total) {
  796. backwards = false;
  797. }
  798. else if (columns.min === null || visibleColumns + columns.getPaddingCount() >= columns.min) {
  799. break;
  800. }
  801. else {
  802. paddingCount = paddingCount + 1;
  803. }
  804. }
  805. // when moving backwards try fitting left columns first
  806. if (backwards && columnNumber > 0) {
  807. columnNumber = columnNumber - 1;
  808. }
  809. if (columnNumber + visibleColumns < columns.total) {
  810. visibleColumns = visibleColumns + 1;
  811. }
  812. }
  813. var lastRenderableColumn = {
  814. columnNumber: columnNumber,
  815. visibleColumns: visibleColumns,
  816. paddingCount: paddingCount
  817. };
  818. if (columnHistory.length > 0) {
  819. lastRenderableColumn = columnHistory[columnHistory.length - 1];
  820. }
  821. columns.setVisibleColumns(
  822. lastRenderableColumn.columnNumber,
  823. lastRenderableColumn.visibleColumns,
  824. lastRenderableColumn.paddingCount);
  825. if (pagedTable.offsetWidth > 0) {
  826. page.setVisiblePages(Math.max(Math.ceil(1.0 * (pagedTable.offsetWidth - 250) / 40), 2));
  827. }
  828. registerWidths();
  829. };
  830. me.fit = function(startBackwards) {
  831. me.fitRows();
  832. me.fitColumns(startBackwards);
  833. }
  834. me.render = function() {
  835. me.fitColumns(false);
  836. // render header/footer to measure height accurately
  837. renderHeader();
  838. renderFooter();
  839. me.fitRows();
  840. renderBody();
  841. // re-render footer to match new rows
  842. renderFooter();
  843. }
  844. var resizeLastWidth = -1;
  845. var resizeLastHeight = -1;
  846. var resizeNewWidth = -1;
  847. var resizeNewHeight = -1;
  848. var resizePending = false;
  849. me.resize = function(newWidth, newHeight) {
  850. function resizeDelayed() {
  851. resizePending = false;
  852. if (
  853. (resizeNewWidth !== resizeLastWidth) ||
  854. (!me.fixedHeight() && resizeNewHeight !== resizeLastHeight)
  855. ) {
  856. resizeLastWidth = resizeNewWidth;
  857. resizeLastHeight = resizeNewHeight;
  858. setTimeout(resizeDelayed, 200);
  859. resizePending = true;
  860. } else {
  861. me.render();
  862. triggerOnChange();
  863. resizeLastWidth = -1;
  864. resizeLastHeight = -1;
  865. }
  866. }
  867. resizeNewWidth = newWidth;
  868. resizeNewHeight = newHeight;
  869. if (!resizePending) resizeDelayed();
  870. };
  871. };
  872. var PagedTableDoc;
  873. (function (PagedTableDoc) {
  874. var allPagedTables = [];
  875. PagedTableDoc.initAll = function() {
  876. allPagedTables = [];
  877. var pagedTables = [].slice.call(document.querySelectorAll('[data-pagedtable="false"],[data-pagedtable=""]'));
  878. pagedTables.forEach(function(pagedTable, idx) {
  879. pagedTable.setAttribute("data-pagedtable", "true");
  880. pagedTable.setAttribute("pagedtable-page", 0);
  881. pagedTable.setAttribute("class", "pagedtable-wrapper");
  882. var pagedTableInstance = new PagedTable(pagedTable);
  883. pagedTableInstance.init();
  884. allPagedTables.push(pagedTableInstance);
  885. });
  886. };
  887. PagedTableDoc.resizeAll = function() {
  888. allPagedTables.forEach(function(pagedTable) {
  889. pagedTable.render();
  890. });
  891. };
  892. window.addEventListener("resize", PagedTableDoc.resizeAll);
  893. return PagedTableDoc;
  894. })(PagedTableDoc || (PagedTableDoc = {}));
  895. window.onload = function() {
  896. PagedTableDoc.initAll();
  897. };