Wikipedia Extender

Adds: sidebar toggle bar (click on blue vertical separator bar), auto collapsing sticky floating table of contents at top left (hover mouse on it to expand), collapsible sections (click section title to toggle), section links (for getting URL to page sections), auto popup sticky table headers (when table height is longer than the screen), table manipulations (double click empty space of a table cell to show menu).

当前为 2021-08-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Wikipedia Extender
  3. // @namespace https://greasyfork.org/en/users/85671-jcunews
  4. // @version 1.0.8
  5. // @license GNU AGPLv3
  6. // @author jcunews
  7. // @description Adds: sidebar toggle bar (click on blue vertical separator bar), auto collapsing sticky floating table of contents at top left (hover mouse on it to expand), collapsible sections (click section title to toggle), section links (for getting URL to page sections), auto popup sticky table headers (when table height is longer than the screen), table manipulations (double click empty space of a table cell to show menu).
  8. // @match *://*.wikipedia.org/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (() => {
  13.  
  14. //*** CONFIGURATION BEGIN ***
  15.  
  16. //Set to `true` to automatically hide the Wikipedia sidebar.
  17. var autohideSidebar = false;
  18.  
  19. //*** CONFIGURATION END ***
  20.  
  21. var sty = document.head.appendChild(document.createElement("STYLE")), eContent = document.querySelector("#content"), mwp = window["mw-panel"];
  22.  
  23. //*** add sidebar toggle bar ***
  24.  
  25. if (mwp) {
  26. //init sidebar bar
  27. sty.textContent += `
  28. #mw-panel {overflow:hidden!important}
  29. #sbt {position:absolute; margin-top:-1px; width:6px; background:#adf; cursor:pointer}`;
  30. var sbt = document.createElement("DIV");
  31. sbt.id = "sbt";
  32. sbt.onclick = () => {
  33. if (!mwp.style.display) { //hide
  34. mwp.style.display = "none";
  35. sbt.style.left = "0";
  36. eContent.style.marginLeft = "5px";
  37. } else { //unhide
  38. mwp.style.display = "";
  39. sbt.style.left = (mwp.offsetWidth + 3) + "px";
  40. eContent.style.marginLeft = "";
  41. }
  42. sbt.style.height = eContent.offsetHeight + "px";
  43. updateTableHeadersX();
  44. updateTableHeadersPos();
  45. };
  46.  
  47. var t = new Date;
  48. function updSidebarBarPos() {
  49. if (eContent.offsetLeft > 8) {
  50. sbt.style.left = eContent.offsetLeft + "px";
  51. sbt.style.height = eContent.offsetHeight + "px";
  52. } else setTimeout(updSidebarBarPos, 100);
  53. }
  54.  
  55. updSidebarBarPos();
  56. eContent.parentNode.insertBefore(sbt, eContent);
  57. addEventListener("resize", updSidebarBarPos);
  58.  
  59. if (autohideSidebar) {
  60. setTimeout(() => {
  61. sbt.click()
  62. }, 20);
  63. }
  64.  
  65. }
  66.  
  67. //*** end: add sidebar toggle bar ***
  68.  
  69.  
  70. //*** add floating table of contents ***
  71.  
  72. //add sticky toc, if has toc
  73. var stoc, ele = window.toc;
  74. if (ele && (ele.childElementCount > 1)) {
  75. sty.textContent += `
  76. #toc.sticky {position:fixed; z-index:10; left:0; top: 0; padding:0 .5ex; background:#f9f9f9; white-space:nowrap; font-family:sans-serif; font-size:10pt}
  77. #toc.sticky #toctitle {margin:.3ex 0 .2ex 0}
  78. #toc.sticky #toctitle h2 {font-family:sans-serif}
  79. #toc.sticky > ul {display:none; margin-bottom:0; max-height:${innerHeight - eContent.offsetTop - 30}px; padding-right:3ex}
  80. #toc.sticky:hover #toctitle {width:auto}
  81. #toc.sticky:hover > ul {display:block; overflow-y:auto}`;
  82. (stoc = ele.cloneNode(true)).classList.add("sticky");
  83. (ele = stoc.querySelector(".toctogglespan")) && ele.remove();
  84. document.body.appendChild(stoc);
  85. }
  86.  
  87. //*** end: add floating table of contents ***
  88.  
  89.  
  90. //*** add collapsible sections and section links ***
  91.  
  92. //add section toggle on section headlines. and add permalink if possible
  93. var a, b, c;
  94.  
  95. function toggleSection() {
  96. var ele = this.parentNode, htyp = ele.tagName, hstat;
  97. ele = ele.nextElementSibling;
  98. if (ele) {
  99. hstat = ele.style.display ? "" : "none";
  100. while (ele) {
  101. if (((ele.tagName[0] === "H") && (ele.tagName[1] <= htyp[1])) || ((ele.tagName === "DIV") && (ele.className === "navbox"))) break;
  102. ele.style.display = hstat;
  103. ele = ele.nextElementSibling;
  104. }
  105. }
  106. updateTableHeadersPos();
  107. }
  108.  
  109. sty.textContent += '.mw-headline:hover {background:#ddf; cursor:pointer}';
  110. document.querySelectorAll(".mw-headline").forEach(a => {
  111. a.onclick = toggleSection;
  112. if (c = a.id ? a.id : (a.name ? a.name : "")) {
  113. (b = document.createElement("SPAN")).className = "mw-editsection";
  114. b.innerHTML = `<span class="mw-editsection-bracket">[</span><a href="${location.href.split("#")[0] + "#" + c}" title="Link to this section">link</a><span class="mw-editsection-bracket">]</span>`;
  115. a.parentNode.appendChild(b);
  116. }
  117. });
  118.  
  119. //*** end: add collapsible sections and section links ***
  120.  
  121.  
  122. //*** add sticky table headers ***
  123.  
  124. var headers = [], headersCount;
  125.  
  126. //sticky table header functions
  127. function cssPxValToNumber(s) {
  128. return parseInt(s.match(/^(\d+)/)[1]);
  129. }
  130.  
  131. function getDocumentXY(ele) {
  132. var a = [ele.offsetLeft, ele.offsetTop];
  133. ele = ele.offsetParent;
  134. while(ele) {
  135. a[0] += ele.offsetLeft;
  136. a[1] += ele.offsetTop;
  137. ele = ele.offsetParent;
  138. }
  139. return a;
  140. }
  141.  
  142. function updateTableDimensions(hdr) {
  143. var i, j, k, l, lc, css, tbl = hdr.table, tc = tbl.rows[0].cells, scrLeft = document.documentElement.scrollLeft;
  144. css = getComputedStyle(tc[0]);
  145. k = (cssPxValToNumber(css.borderLeftWidth) + cssPxValToNumber(css.borderRightWidth) + cssPxValToNumber(css.paddingLeft) + cssPxValToNumber(css.paddingRight)) + 1;
  146. hdr.style.width = tbl.offsetWidth + "px";
  147. hdr.style.height = tc[0].offsetHeight + "px";
  148. lc = (l = Array.from(hdr.rows[0].cells)).length;
  149. l.forEach((c, j) => c.style.width = (tc[j].offsetWidth - k + ((j * 2) >= lc ? 1 : 0)) + "px");
  150. j = getDocumentXY(tbl);
  151. hdr.absLeft = j[0];
  152. hdr.absTop = j[1];
  153. hdr.absRight = (hdr.absLeft + tbl.offsetWidth) - hdr.offsetWidth;
  154. j = tbl.rows;
  155. l = j.length;
  156. k = j[l - 1].cells;
  157. hdr.absBottom = (hdr.absTop + tbl.offsetHeight) - ((k[k.length - 1].tagName === "TH") && (l >= 3) ? k[0].offsetHeight + j[l - 2].cells[0].offsetHeight : 0) - hdr.offsetHeight;
  158. }
  159.  
  160. function updateTableHeadersX() {
  161. var scrLeft = document.documentElement.scrollLeft;
  162. headers.forEach(h => {
  163. updateTableDimensions(h);
  164. h.style.left = (h.absLeft - scrLeft - 1) + "px";
  165. });
  166. }
  167.  
  168. function updateTableHeadersPos() {
  169. var scrLeft = document.documentElement.scrollLeft, scrTop = document.documentElement.scrollTop;
  170. headers.forEach(h => {
  171. if ((scrTop > h.absTop) && (scrTop < h.absBottom)) {
  172. h.style.display = "";
  173. h.style.left = (h.absLeft - scrLeft - 1) + "px";
  174. } else h.style.display = "none";
  175. });
  176. updateTableHeadersX();
  177. }
  178.  
  179. function tableHeaderClick(ev) {
  180. if (ev.target.className !== "headerSort") return;
  181. this.table.rows[0].cells[ev.target.cellIndex].click();
  182. Array.from(this.rows[0].cells).forEach(c => c.className = this.table.rows[0].cells[c.cellIndex].className);
  183. }
  184.  
  185. //add sticky table headers for any table
  186. (function addStickyTblHdr() {
  187. var a, tables = document.querySelectorAll(".wikitable"), i, tbl, hdr, j, css, k, l = tables.length;
  188. if (!tables.length) return;
  189. tables.forEach((tbl, i) => {
  190. if ((tbl.rows[0].cells.length < 2) || (tbl.rows[0].cells[1].tagName !== "TH")) return;
  191. (hdr = document.createElement("TABLE")).sticky = true;
  192. hdr.table = tbl;
  193. tbl.hdr = hdr;
  194. Array.from(tbl.attributes).forEach(a => hdr.setAttribute(a.name, a.value)); //copy table attributes
  195. hdr.id = "";
  196. hdr.style.cssText = tbl.style.cssText + ";position:absolute;z-index:9;margin:" + getComputedStyle(tbl).margin + ";border:2px solid#000;width:" + tbl.offsetWidth + "px;height:" + (tbl.rows[0].cells[0].offsetHeight + 2) + "px;table-layout:fixed";
  197. a = tbl.rows[0].outerHTML; //first header row
  198. if (tbl.rows[1] && tbl.rows[1].lastElementChild && (tbl.rows[1].lastElementChild.tagName === "TH")) a += tbl.rows[1].outerHTML; //second header row
  199. hdr.innerHTML = a;
  200. headers.push(tbl.parentNode.insertBefore(hdr, tbl));
  201. updateTableDimensions(hdr);
  202. hdr.style.display = "none";
  203. hdr.style.position = "fixed";
  204. hdr.style.top = "0";
  205. hdr.style.margin = "0";
  206. hdr.addEventListener("click", tableHeaderClick);
  207. });
  208. headersCount = headers.length;
  209. updateTableHeadersPos();
  210. addEventListener("scroll", updateTableHeadersPos);
  211. addEventListener("resize", updateTableHeadersX);
  212. })();
  213.  
  214. //*** end: add sticky table headers ***
  215.  
  216.  
  217. //*** add table manipulations ***
  218.  
  219. var tcp = document.createElement("DIV"), tcpWid, tcpHei;
  220.  
  221. function sortTable(table, colidx, numeric, start, count) {
  222.  
  223. function sort2(func, start, count) {
  224. if (!func || !table.childElementCount) return;
  225. var tbl = table.children[0], ar, tmp, i;
  226. while (tbl && (tbl.tagName !== "TBODY")) tbl = tbl.nextElementSibling;
  227. if (!tbl.rows.length) return;
  228. if (isNaN(start)) start = tbl.rows[0].cells.length && (tbl.rows[0].cells[0].tagName === "TH") ? 1 : 0;
  229. if (start < 0) start = 0;
  230. if (isNaN(count) || (count <= 0)) count = tbl.rows.length - start;
  231. if (count === table.rows.length) {
  232. ar = Array.from(tbl.rows);
  233. } else ar = Array.from(tbl.rows).splice(start, count);
  234. ar.sort(func);
  235. tmp = document.createElement("TBODY");
  236. for (i = 0; i < start; i++) tmp.appendChild(tbl.rows[i].cloneNode(true));
  237. for (i = 0; i < ar.length; i++) tmp.appendChild(ar[i].cloneNode(true));
  238. for (i = (start + count); i < tbl.rows.length; i++) tmp.appendChild(tbl.rows[i].cloneNode(true));
  239. table.replaceChild(tmp, tbl);
  240. }
  241.  
  242. function stripComma(s) {
  243. var a = s.valueOf(), b = a.lastIndexOf(",");
  244. while (b > 0) {
  245. a = a.substr(0, b) + a.substr(b + 1, 99);
  246. b = a.lastIndexOf(",");
  247. }
  248. return a;
  249. }
  250.  
  251. if (isNaN(colidx) || (colidx < 0)) colidx = 0;
  252. sort2(function(i1, i2) {
  253. var t1, t2, n1, n2;
  254. if (i1.cells.length > colidx) {
  255. t1 = i1.cells[colidx].textContent
  256. } else t1 = "";
  257. if (i2.cells.length > colidx) {
  258. t2 = i2.cells[colidx].textContent
  259. } else t2 = "";
  260. if (numeric) {
  261. n1 = parseFloat(stripComma(t1));
  262. n2 = parseFloat(stripComma(t2));
  263. if (!isNaN(n1)) {
  264. return isNaN(n2) ? -1 : n1 - n2;
  265. } else if (!isNaN(n2)) return 1;
  266. }
  267. t1 = t1.toLowerCase();
  268. t2 = t2.toLowerCase();
  269. if (t1 < t2) {
  270. return -1
  271. } else if (t1 > t2) {
  272. return 1
  273. } else return 0;
  274. }, start, count);
  275. }
  276.  
  277. function docClick(ev) {
  278. var a, ele = ev.target, table, rows, cell, rowStart, rowEnd, rowCount;
  279. if ((ev.target === tcp) || !tcp.parentNode) return;
  280. table = tcp.table;
  281. rows = table.rows;
  282. cell = tcp.cell;
  283. hdrRow = table.hdr.rows[0];
  284. tcp.style.opacity = ".666";
  285. tcp.cell.style.background = tcp.cell.style.backgroundz ? tcp.cell.style.backgroundz : "";
  286. if (a = ele.attributes["tcpcmd"]) { //command element
  287. rowStart = 0;
  288. while (rows[rowStart].cells[rows[rowStart].cells.length - 1].tagName === "TH") rowStart++;
  289. rowEnd = rows.length - 1;
  290. while (rows[rowEnd].cells[rows[rowEnd].cells.length - 1].tagName === "TH") rowEnd--;
  291. rowCount = rowStart + rowEnd + 1;
  292. setTimeout(() => {
  293. var i, j, ci = cell.cellIndex, txt = cell.textContent.trim().toLowerCase(), cnt = 0;
  294. switch(a.value) {
  295. case "SortCol":
  296. sortTable(table, cell.cellIndex, false, rowStart, rowCount);
  297. break;
  298. case "SortColNum":
  299. sortTable(table, cell.cellIndex, true, rowStart, rowCount);
  300. break;
  301. case "MoveColFirst":
  302. if (ci === 0) break;
  303. Array.from(rows).forEach((r, z) => {
  304. try {
  305. r.insertBefore(r.cells[ci], r.cells[0]);
  306. } catch(z) {}
  307. });
  308. hdrRow.insertBefore(hdrRow.cells[ci], hdrRow.cells[0]);
  309. break;
  310. case "MoveColLeft":
  311. if (ci === 0) break;
  312. Array.from(rows).forEach((r, z) => {
  313. try {
  314. r.insertBefore(r.cells[ci], r.cells[ci - 1]);
  315. } catch(z) {}
  316. });
  317. hdrRow.insertBefore(hdrRow.cells[ci], hdrRow.cells[ci - 1]);
  318. break;
  319. case "MoveColRight":
  320. if (ci === (rows[0].cells.length - 1)) break;
  321. Array.from(rows).forEach((r, z) => {
  322. try {
  323. r.insertBefore(r.cells[ci], r.cells[ci + 2]);
  324. } catch(z) {}
  325. });
  326. hdrRow.insertBefore(hdrRow.cells[ci], hdrRow.cells[ci + 2]);
  327. break;
  328. case "MoveColLast":
  329. if (ci === (rows[0].cells.length - 1)) break;
  330. Array.from(rows).forEach((r, z) => {
  331. try {
  332. r.appendChild(r.cells[ci]);
  333. } catch(z) {}
  334. });
  335. hdrRow.appendChild(hdrRow.cells[ci]);
  336. break;
  337. case "HidCol":
  338. Array.from(rows).forEach((r, z) => {
  339. try {
  340. r.cells[ci].style.display = "none";
  341. } catch(z) {}
  342. });
  343. hdrRow.cells[ci].style.display = "none";
  344. break;
  345. case "HidRow":
  346. cell.parentNode.style.display = "none";
  347. break;
  348. case "HidRowSamTxt":
  349. for (i = rowEnd; i >= rowStart; i--) {
  350. try {
  351. if ((rows[i].cells[ci].textContent.trim().toLowerCase() === txt) && (rows[i].style.display !== "none")) cnt++;
  352. } catch(z) {}
  353. }
  354. if (!confirm("Hide " + cnt + " matching rows?")) break;
  355. for (i = rowEnd; i >= rowStart; i--) {
  356. try {
  357. if (rows[i].cells[ci].textContent.trim().toLowerCase() === txt) rows[i].style.display = "none";
  358. } catch(z) {}
  359. }
  360. break;
  361. case "HidRowConTxt":
  362. txt = prompt("Enter search text.", txt);
  363. if (txt === null) break;
  364. if (txt === "") {
  365. alert("Search text can not be empty.");
  366. break;
  367. }
  368. for (i = rowEnd; i >= rowStart; i--) {
  369. try {
  370. if ((rows[i].cells[ci].textContent.trim().toLowerCase().indexOf(txt) >= 0) && (rows[i].style.display !== "none")) cnt++;
  371. } catch(z) {}
  372. }
  373. if (!confirm("Hide " + cnt + " matching rows?")) break;
  374. for (i = rowEnd; i >= rowStart; i--) {
  375. try {
  376. if (rows[i].cells[ci].textContent.trim().toLowerCase().indexOf(txt) >= 0) rows[i].style.display = "none";
  377. } catch(z) {}
  378. }
  379. break;
  380. case "HidRowNotSamTxt":
  381. for (i = rowEnd; i >= rowStart; i--) {
  382. try {
  383. if ((rows[i].cells[ci].textContent.trim().toLowerCase() !== txt) && (rows[i].style.display !== "none")) cnt++;
  384. } catch(z) {}
  385. }
  386. if (!confirm("Hide " + cnt + " matching rows?")) break;
  387. for (i = rowEnd; i >= rowStart; i--) {
  388. try {
  389. if (rows[i].cells[ci].textContent.trim().toLowerCase() !== txt) rows[i].style.display = "none";
  390. } catch(z) {}
  391. }
  392. break;
  393. case "HidRowNotConTxt":
  394. txt = prompt("Enter search text.", txt);
  395. if (txt === null) break;
  396. if (txt === "") {
  397. alert("Search text can not be empty.");
  398. break;
  399. }
  400. for (i = rowEnd; i >= rowStart; i--) {
  401. try {
  402. if ((rows[i].cells[ci].textContent.trim().toLowerCase().indexOf(txt) < 0) && (rows[i].style.display !== "none")) cnt++;
  403. } catch(z) {}
  404. }
  405. if (!confirm("Hide " + cnt + " matching rows?")) break;
  406. for (i = rowEnd; i >= rowStart; i--) {
  407. try {
  408. if(rows[i].cells[ci].textContent.trim().toLowerCase().indexOf(txt) < 0) rows[i].style.display = "none";
  409. } catch(z) {}
  410. }
  411. break;
  412. case "UnhideAll":
  413. for (i = rows.length - 1; i >= 0; i--) {
  414. for (j = rows[i].cells.length - 1; j >= 0; j--) {
  415. try {
  416. rows[i].cells[j].style.display = "";
  417. } catch(z) {}
  418. }
  419. try {
  420. rows[i].style.display = "";
  421. } catch(z) {}
  422. }
  423. for (j = hdrRow.cells.length - 1; j >= 0; j--) {
  424. try {
  425. hdrRow.cells[j].style.display = "";
  426. } catch(z) {}
  427. }
  428. break;
  429. case "ExpToCsv":
  430. a = false;
  431. (b = document.createElement("A")).href = "data:text/comma-separated-values;charset=utf-8;base64," + btoa(
  432. encodeURIComponent(Array.from(rows).reduce((p, r, i, ar) => {
  433. if (!r.cells.length) return p;
  434. if (r.cells[r.cells.length - 1].tagName === "TD") {
  435. a = true;
  436. } else if (a) return p;
  437. p.push(Array.from(r.cells).map((c, t) => {
  438. t = c.textContent.replace(/^\s+|\s+$/g, "");
  439. if (!(/[",]/).test(t)) {
  440. return t;
  441. } else return '"' + t.replace(/"/g, '""') + '"';
  442. }));
  443. return p;
  444. }, []).join("\n") + "\n").replace(/%([0-9A-F]{2})/g, (m, p) => String.fromCharCode('0x' + p))
  445. );
  446. b.download = "table.csv";
  447. b.style.display = "none";
  448. document.body.appendChild(b).click();
  449. b.remove();
  450. break;
  451. }
  452. updSidebarBarPos();
  453. updateTableHeadersX();
  454. }, 0);
  455. }
  456. tcp.parentNode.removeChild(tcp);
  457. tcp.style.opacity = 0;
  458. }
  459.  
  460. function getNewBgColor(ele) {
  461. var clr = getComputedStyle(ele).backgroundColor, r, g, b, rx;
  462. while (clr === "transparent") {
  463. ele = ele.parentNode;
  464. if (!ele) return "#dfd";
  465. clr = getComputedStyle(ele).backgroundColor;
  466. }
  467. rx = clr.match(/rgb\((\d+), (\d+), (\d+)\)/i);
  468. if(!rx) {
  469. rx = clr.match(/#([0-9a-f])([0-9a-f])([0-9a-f])/i);
  470. if (rx) {
  471. rx[1] = parseInt(rx[1], 16);
  472. rx[2] = parseInt(rx[2], 16);
  473. rx[3] = parseInt(rx[3], 16);
  474. } else return "#dfd";
  475. }
  476. if ((rx[2] > rx[1]) && (rx[2] > rx[3])) return "#fdd";
  477. return "#dfd";
  478. }
  479.  
  480. function showTableCtrlPanel(ev) {
  481. var cell = ev.target, tbl, tblX, tblY, rows, cells, row, cellX, cellY, cellWidth, cellHeight, hdrTop, hdrBottom, pos, posDelta;
  482. while (!(/^(TH|TD)$/).test(cell.tagName)) {
  483. cell = cell.parentNode;
  484. if (!cell) return;
  485. }
  486. rows = (tbl = (row = cell.parentNode).parentNode.parentNode).rows;
  487. cells = rows[0].cells;
  488. if (tbl.sticky) return;
  489. tblX = tbl.offsetLeft;
  490. tblY = tbl.offsetTop;
  491. cellX = cell.offsetLeft;
  492. cellY = cell.offsetTop;
  493. cellWidth = cell.offsetWidth;
  494. cellHeight = cell.offsetHeight;
  495. cell.style.background = getNewBgColor(cell);
  496. tcp.table = tbl;
  497. tcp.cell = cell;
  498. hdrTop = cells[0].tagName === "TH";
  499. hdrBottom = cells[cells.length - 1].tagName === "TH";
  500. tcp.info.textContent = "Row " + (row.rowIndex + 1) + "/" + rows.length + ", Cell " + (cell.cellIndex + 1) + "/" + row.cells.length + ", " + (hdrTop ? (hdrBottom ? "Top+Bottom" : "Top") : (hdrBottom ? "Bottom" : "No")) + " Header";
  501. tbl.parentNode.insertBefore(tcp, tbl);
  502. if (tcp.style.opacity === "0") {
  503. tcpWid = tcp.offsetWidth;
  504. tcpHei = tcp.offsetHeight;
  505. }
  506. pos = tblX + cellX + Math.floor(cellWidth / 2);
  507. tcp.style.left = pos + "px";
  508. posDelta = (getDocumentXY(tcp)[0] + tcpWid - scrollX) - innerWidth;
  509. if (posDelta > 0) tcp.style.left = ((innerWidth + (posDelta - tcpWid)) >= tcpWid ? pos - tcpWid : pos - posDelta) + "px";
  510. pos = tblY + cellY + cellHeight;
  511. tcp.style.top = pos + "px";
  512. posDelta = (getDocumentXY(tcp)[1] + tcpHei - scrollY) - innerHeight;
  513. if (posDelta > 0) tcp.style.top = ((innerHeight + (posDelta - tcpHei - cellHeight)) >= tcpHei ? pos - tcpHei - cellHeight : pos - posDelta) + "px";
  514. tcp.style.opacity = "";
  515. }
  516.  
  517. sty.textContent += `
  518. #tcp {position:absolute; z-index:999; border:1px solid; background:#c3d4e5}
  519. #tcp div {border-bottom:1px solid; padding:0 4px; background: ddd; white-space:nowrap}
  520. #tcp a {display:block; padding:0 4px; white-space:nowrap; text-decoration:none}
  521. #tcp a:hover {background:#def}
  522. #tcp hr {margin:0}`;
  523. tcp.id = "tcp";
  524. tcp.style.opacity = 0;
  525. tcp.innerHTML = `<div></div>
  526. <a tcpcmd="SortCol" href="javascript:void(0)">Sort column</a>
  527. <a tcpcmd="SortColNum" href="javascript:void(0)">Sort column (numeric)</a>
  528. <hr />
  529. <a tcpcmd="MoveColFirst" href="javascript:void(0)">Move column to leftmost</a>
  530. <a tcpcmd="MoveColLeft" href="javascript:void(0)">Move column to left</a>
  531. <a tcpcmd="MoveColRight" href="javascript:void(0)">Move column to right</a>
  532. <a tcpcmd="MoveColLast" href="javascript:void(0)">Move column to rightmost</a>
  533. <hr />
  534. <a tcpcmd="HidCol" href="javascript:void(0)">Hide column</a>
  535. <hr />
  536. <a tcpcmd="HidRow" href="javascript:void(0)">Hide row</a>
  537. <a tcpcmd="HidRowSamTxt" href="javascript:void(0)">Hide row if cell has same text</a>
  538. <a tcpcmd="HidRowConTxt" href="javascript:void(0)">Hide row if cell contains...</a>
  539. <a tcpcmd="HidRowNotSamTxt" href="javascript:void(0)">Hide row if cell doesn't have same text</a>
  540. <a tcpcmd="HidRowNotConTxt" href="javascript:void(0)">Hide row if cell doesn't contain...</a>
  541. <hr />
  542. <a tcpcmd="UnhideAll" href="javascript:void(0)">Unhide All Hidden Rows/Columns</a>
  543. <hr />
  544. <a tcpcmd="ExpToCsv" href="javascript:void(0)">Export to CSV</a>`;
  545. tcp.info = tcp.firstElementChild;
  546. tcp.onclick = docClick;
  547. document.addEventListener("click", docClick);
  548. document.addEventListener("dblclick", showTableCtrlPanel);
  549.  
  550. //*** end: add table manipulations ***
  551.  
  552. })();