pdf_outline_viewer.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2020 Mozilla Foundation
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. * @licend The above is the entire license notice for the
  20. * Javascript code in this page
  21. */
  22. "use strict";
  23. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.PDFOutlineViewer = void 0;
  27. var _pdf = require("../pdf");
  28. const DEFAULT_TITLE = "\u2013";
  29. class PDFOutlineViewer {
  30. constructor({
  31. container,
  32. linkService,
  33. eventBus
  34. }) {
  35. this.container = container;
  36. this.linkService = linkService;
  37. this.eventBus = eventBus;
  38. this.reset();
  39. eventBus._on("toggleoutlinetree", this.toggleOutlineTree.bind(this));
  40. }
  41. reset() {
  42. this.outline = null;
  43. this.lastToggleIsShow = true;
  44. this.container.textContent = "";
  45. this.container.classList.remove("outlineWithDeepNesting");
  46. }
  47. _dispatchEvent(outlineCount) {
  48. this.eventBus.dispatch("outlineloaded", {
  49. source: this,
  50. outlineCount
  51. });
  52. }
  53. _bindLink(element, {
  54. url,
  55. newWindow,
  56. dest
  57. }) {
  58. const {
  59. linkService
  60. } = this;
  61. if (url) {
  62. (0, _pdf.addLinkAttributes)(element, {
  63. url,
  64. target: newWindow ? _pdf.LinkTarget.BLANK : linkService.externalLinkTarget,
  65. rel: linkService.externalLinkRel,
  66. enabled: linkService.externalLinkEnabled
  67. });
  68. return;
  69. }
  70. element.href = linkService.getDestinationHash(dest);
  71. element.onclick = () => {
  72. if (dest) {
  73. linkService.navigateTo(dest);
  74. }
  75. return false;
  76. };
  77. }
  78. _setStyles(element, {
  79. bold,
  80. italic
  81. }) {
  82. if (bold) {
  83. element.style.fontWeight = "bold";
  84. }
  85. if (italic) {
  86. element.style.fontStyle = "italic";
  87. }
  88. }
  89. _addToggleButton(div, {
  90. count,
  91. items
  92. }) {
  93. const toggler = document.createElement("div");
  94. toggler.className = "outlineItemToggler";
  95. if (count < 0 && Math.abs(count) === items.length) {
  96. toggler.classList.add("outlineItemsHidden");
  97. }
  98. toggler.onclick = evt => {
  99. evt.stopPropagation();
  100. toggler.classList.toggle("outlineItemsHidden");
  101. if (evt.shiftKey) {
  102. const shouldShowAll = !toggler.classList.contains("outlineItemsHidden");
  103. this._toggleOutlineItem(div, shouldShowAll);
  104. }
  105. };
  106. div.insertBefore(toggler, div.firstChild);
  107. }
  108. _toggleOutlineItem(root, show = false) {
  109. this.lastToggleIsShow = show;
  110. for (const toggler of root.querySelectorAll(".outlineItemToggler")) {
  111. toggler.classList.toggle("outlineItemsHidden", !show);
  112. }
  113. }
  114. toggleOutlineTree() {
  115. if (!this.outline) {
  116. return;
  117. }
  118. this._toggleOutlineItem(this.container, !this.lastToggleIsShow);
  119. }
  120. render({
  121. outline
  122. }) {
  123. let outlineCount = 0;
  124. if (this.outline) {
  125. this.reset();
  126. }
  127. this.outline = outline || null;
  128. if (!outline) {
  129. this._dispatchEvent(outlineCount);
  130. return;
  131. }
  132. const fragment = document.createDocumentFragment();
  133. const queue = [{
  134. parent: fragment,
  135. items: this.outline
  136. }];
  137. let hasAnyNesting = false;
  138. while (queue.length > 0) {
  139. const levelData = queue.shift();
  140. for (const item of levelData.items) {
  141. const div = document.createElement("div");
  142. div.className = "outlineItem";
  143. const element = document.createElement("a");
  144. this._bindLink(element, item);
  145. this._setStyles(element, item);
  146. element.textContent = (0, _pdf.removeNullCharacters)(item.title) || DEFAULT_TITLE;
  147. div.appendChild(element);
  148. if (item.items.length > 0) {
  149. hasAnyNesting = true;
  150. this._addToggleButton(div, item);
  151. const itemsDiv = document.createElement("div");
  152. itemsDiv.className = "outlineItems";
  153. div.appendChild(itemsDiv);
  154. queue.push({
  155. parent: itemsDiv,
  156. items: item.items
  157. });
  158. }
  159. levelData.parent.appendChild(div);
  160. outlineCount++;
  161. }
  162. }
  163. if (hasAnyNesting) {
  164. this.container.classList.add("outlineWithDeepNesting");
  165. this.lastToggleIsShow = fragment.querySelectorAll(".outlineItemsHidden").length === 0;
  166. }
  167. this.container.appendChild(fragment);
  168. this._dispatchEvent(outlineCount);
  169. }
  170. }
  171. exports.PDFOutlineViewer = PDFOutlineViewer;