deepresearch-flow 0.4.1__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. deepresearch_flow/paper/db.py +34 -0
  2. deepresearch_flow/paper/db_ops.py +21 -7
  3. deepresearch_flow/paper/prompt_templates/deep_read_phi_system.j2 +6 -0
  4. deepresearch_flow/paper/prompt_templates/deep_read_phi_user.j2 +391 -0
  5. deepresearch_flow/paper/prompt_templates/eight_questions_phi_system.j2 +6 -0
  6. deepresearch_flow/paper/prompt_templates/eight_questions_phi_user.j2 +133 -0
  7. deepresearch_flow/paper/prompt_templates/simple_phi_system.j2 +6 -0
  8. deepresearch_flow/paper/prompt_templates/simple_phi_user.j2 +31 -0
  9. deepresearch_flow/paper/schemas/deep_read_phi_schema.json +30 -0
  10. deepresearch_flow/paper/template_registry.py +39 -0
  11. deepresearch_flow/paper/templates/deep_read_phi.md.j2 +40 -0
  12. deepresearch_flow/paper/web/app.py +106 -1
  13. deepresearch_flow/paper/web/constants.py +1 -0
  14. deepresearch_flow/paper/web/handlers/__init__.py +2 -1
  15. deepresearch_flow/paper/web/handlers/api.py +55 -0
  16. deepresearch_flow/paper/web/handlers/pages.py +105 -25
  17. deepresearch_flow/paper/web/markdown.py +230 -4
  18. deepresearch_flow/paper/web/pdfjs/web/viewer.html +57 -5
  19. deepresearch_flow/paper/web/pdfjs/web/viewer.js +5 -1
  20. deepresearch_flow/paper/web/static/css/main.css +8 -1
  21. deepresearch_flow/paper/web/static/js/detail.js +527 -124
  22. deepresearch_flow/paper/web/static/js/outline.js +48 -34
  23. deepresearch_flow/paper/web/static_assets.py +289 -0
  24. deepresearch_flow/paper/web/templates/detail.html +52 -66
  25. deepresearch_flow/paper/web/templates.py +7 -4
  26. deepresearch_flow/paper/web/text.py +8 -4
  27. deepresearch_flow/recognize/organize.py +9 -12
  28. deepresearch_flow/translator/fixers.py +15 -0
  29. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/METADATA +62 -2
  30. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/RECORD +34 -25
  31. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/WHEEL +0 -0
  32. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/entry_points.txt +0 -0
  33. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/licenses/LICENSE +0 -0
  34. {deepresearch_flow-0.4.1.dist-info → deepresearch_flow-0.5.1.dist-info}/top_level.txt +0 -0
@@ -8,8 +8,7 @@
8
8
 
9
9
  function init() {
10
10
  initFullscreen();
11
- initMarkdownRendering();
12
- initFootnotes();
11
+ initMarkdownContent();
13
12
  initBackToTop();
14
13
 
15
14
  // View-specific initializers
@@ -28,6 +27,12 @@
28
27
  // Back-to-top button
29
28
  // ========================================
30
29
 
30
+ function escapeHtml(text) {
31
+ var div = document.createElement('div');
32
+ div.textContent = String(text || '');
33
+ return div.innerHTML;
34
+ }
35
+
31
36
  function initBackToTop() {
32
37
  var button = document.getElementById('backToTop');
33
38
  if (!button) return;
@@ -81,66 +86,431 @@
81
86
  }
82
87
 
83
88
  // ========================================
84
- // Markdown rendering (Mermaid + KaTeX)
89
+ // Markdown rendering (Marked + DOMPurify) + enhancements
85
90
  // ========================================
86
91
 
87
- function initMarkdownRendering() {
88
- // Only run if we have content with mermaid/katex
89
- if (!document.getElementById('content')) return;
90
-
91
- function initRendering() {
92
- // Markmap: convert fenced markmap blocks to svg mindmaps
93
- if (window.markmap && window.markmap.Transformer && window.markmap.Markmap) {
94
- var transformer = new window.markmap.Transformer();
95
- document.querySelectorAll('code.language-markmap').forEach(function(code) {
96
- var pre = code.parentElement;
97
- if (!pre) return;
98
- var svg = document.createElement('svg');
99
- svg.className = 'markmap';
100
- pre.replaceWith(svg);
101
- try {
102
- var result = transformer.transform(code.textContent || '');
103
- window.markmap.Markmap.create(svg, null, result.root);
104
- } catch (err) {
105
- // Ignore markmap parse errors
106
- }
92
+ var DOMPURIFY_CONFIG = {
93
+ ADD_TAGS: ['sup', 'table', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'caption', 'colgroup', 'col'],
94
+ ADD_ATTR: ['colspan', 'rowspan', 'align'],
95
+ ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto|tel|data:image\/)|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i
96
+ };
97
+
98
+ function initMarkdownContent() {
99
+ var content = document.getElementById('content');
100
+ if (!content) return;
101
+
102
+ var markdownUrl = content.dataset.markdownUrl;
103
+ var rawMarkdownUrl = content.dataset.rawMarkdownUrl;
104
+ var imagesBaseUrl = content.dataset.imagesBaseUrl || '';
105
+ if (!markdownUrl) {
106
+ var rawText = content.textContent || '';
107
+ if (content.children.length === 0 && rawText.trim()) {
108
+ var rendered = renderMarkdown(rawText, imagesBaseUrl);
109
+ content.innerHTML = rendered;
110
+ } else {
111
+ rewriteRenderedImages(imagesBaseUrl);
112
+ }
113
+ if (rawMarkdownUrl) {
114
+ var statusEl = document.getElementById('markdownStatus');
115
+ var rawEl = document.getElementById('rawMarkdown');
116
+ if (statusEl) statusEl.textContent = 'Loading raw markdown...';
117
+ fetch(rawMarkdownUrl).then(function(res) {
118
+ if (!res.ok) throw new Error('Failed to load raw markdown');
119
+ return res.text();
120
+ }).then(function(text) {
121
+ if (rawEl) rawEl.textContent = text;
122
+ if (statusEl) statusEl.remove();
123
+ }).catch(function() {
124
+ if (statusEl) statusEl.textContent = 'Failed to load raw markdown.';
107
125
  });
108
126
  }
127
+ runMarkdownEnhancements();
128
+ initFootnotes();
129
+ dispatchContentUpdated();
130
+ return;
131
+ }
109
132
 
110
- // Mermaid: convert fenced code blocks to mermaid divs
111
- document.querySelectorAll('code.language-mermaid').forEach(function(code) {
112
- var pre = code.parentElement;
113
- var div = document.createElement('div');
114
- div.className = 'mermaid';
115
- div.textContent = code.textContent;
116
- pre.replaceWith(div);
117
- });
133
+ var status = document.getElementById('markdownStatus');
134
+ var rawContainer = document.getElementById('rawMarkdown');
135
+ if (status) status.textContent = 'Loading markdown...';
136
+
137
+ fetch(markdownUrl).then(function(res) {
138
+ if (!res.ok) throw new Error('Failed to load markdown');
139
+ return res.text();
140
+ }).then(function(text) {
141
+ if (rawContainer) rawContainer.textContent = text;
142
+ var html = renderMarkdown(text, imagesBaseUrl);
143
+ content.innerHTML = html;
144
+ if (status) status.remove();
145
+ rewriteRenderedImages(imagesBaseUrl);
146
+ runMarkdownEnhancements();
147
+ initFootnotes();
148
+ dispatchContentUpdated();
149
+ }).catch(function() {
150
+ if (status) status.textContent = 'Failed to load markdown.';
151
+ });
152
+ }
153
+
154
+ function renderMarkdown(markdown, imagesBaseUrl) {
155
+ var normalized = normalizeFootnoteDefinitions(markdown || '');
156
+ var footnoteData = extractFootnotes(normalized);
157
+ var mathData = extractMathPlaceholders(footnoteData.markdown);
158
+
159
+ if (!window.marked) {
160
+ return '<pre><code>' + escapeHtml(markdown) + '</code></pre>';
161
+ }
162
+
163
+ var renderer = new window.marked.Renderer();
164
+ renderer.image = function(href, title, text) {
165
+ var src = rewriteImageUrl(href, imagesBaseUrl);
166
+ if (!src) return '';
167
+ var html = '<img src="' + escapeHtml(src) + '" alt="' + escapeHtml(text || '') + '"';
168
+ if (title) {
169
+ html += ' title="' + escapeHtml(title) + '"';
170
+ }
171
+ return html + ' />';
172
+ };
173
+
174
+ if (window.marked && window.marked.setOptions) {
175
+ window.marked.setOptions({ gfm: true, breaks: false });
176
+ }
177
+ var parsed = window.marked.parse(mathData.text || '', { renderer: renderer });
178
+ parsed = replaceMathPlaceholders(parsed, mathData.placeholders);
179
+ parsed = injectFootnotes(parsed, footnoteData);
180
+ if (window.DOMPurify) {
181
+ return window.DOMPurify.sanitize(parsed, DOMPURIFY_CONFIG);
182
+ }
183
+ return parsed;
184
+ }
185
+
186
+ function rewriteImageUrl(url, imagesBaseUrl) {
187
+ if (!url) return url;
188
+ if (!imagesBaseUrl) return url;
189
+ if (isAbsoluteUrl(url)) return url;
190
+
191
+ var cleaned = String(url);
192
+ while (cleaned.indexOf('../') === 0) {
193
+ cleaned = cleaned.slice(3);
194
+ }
195
+ cleaned = cleaned.replace(/^\.\//, '');
196
+ cleaned = cleaned.replace(/^\/+/, '');
197
+ if (cleaned.indexOf('images/') === 0) {
198
+ cleaned = cleaned.slice('images/'.length);
199
+ }
200
+ var base = String(imagesBaseUrl).replace(/\/+$/, '');
201
+ return base + '/' + cleaned;
202
+ }
203
+
204
+ function rewriteRenderedImages(imagesBaseUrl) {
205
+ if (!imagesBaseUrl) return;
206
+ var content = document.getElementById('content');
207
+ if (!content) return;
208
+ content.querySelectorAll('img').forEach(function(img) {
209
+ var src = img.getAttribute('src');
210
+ var rewritten = rewriteImageUrl(src, imagesBaseUrl);
211
+ if (rewritten && rewritten !== src) {
212
+ img.setAttribute('src', rewritten);
213
+ }
214
+ });
215
+ }
216
+
217
+ function isAbsoluteUrl(url) {
218
+ return /^(?:[a-z][a-z0-9+.\-]*:|\/\/|#)/i.test(url) || url.charAt(0) === '/';
219
+ }
118
220
 
119
- if (window.mermaid) {
120
- mermaid.initialize({ startOnLoad: false });
121
- mermaid.run();
221
+ function normalizeFootnoteDefinitions(text) {
222
+ return String(text || '').replace(/^\[\^([^\]]+)\]\s+/gm, '[^$1]: ');
223
+ }
224
+
225
+ function extractFootnotes(text) {
226
+ var lines = String(text || '').split(/\r?\n/);
227
+ var out = [];
228
+ var notes = {};
229
+ var order = [];
230
+ var i = 0;
231
+ while (i < lines.length) {
232
+ var line = lines[i];
233
+ var match = line.match(/^\[\^([^\]]+)\]:\s*(.*)$/);
234
+ if (!match) {
235
+ out.push(line);
236
+ i += 1;
237
+ continue;
238
+ }
239
+ var rawId = match[1].trim();
240
+ var id = sanitizeFootnoteId(rawId);
241
+ var content = match[2] || '';
242
+ var parts = [content];
243
+ i += 1;
244
+ while (i < lines.length) {
245
+ var next = lines[i];
246
+ if (/^\[\^([^\]]+)\]:\s*/.test(next)) {
247
+ break;
248
+ }
249
+ if (/^\s{2,}|\t/.test(next)) {
250
+ parts.push(next.replace(/^\s{2,}|\t/, ''));
251
+ i += 1;
252
+ continue;
253
+ }
254
+ if (next.trim() === '') {
255
+ parts.push('');
256
+ i += 1;
257
+ continue;
258
+ }
259
+ break;
260
+ }
261
+ if (!notes[id]) {
262
+ order.push(id);
122
263
  }
264
+ notes[id] = parts.join('\n').trim();
265
+ }
266
+ return { markdown: out.join('\n'), notes: notes, order: order };
267
+ }
268
+
269
+ function injectFootnotes(html, footnoteData) {
270
+ if (!footnoteData || !footnoteData.order || footnoteData.order.length === 0) {
271
+ return html;
272
+ }
273
+ var notes = footnoteData.notes || {};
274
+ var order = footnoteData.order;
275
+ var replaced = String(html || '').replace(/\[\^([^\]]+)\]/g, function(match, rawId) {
276
+ var id = sanitizeFootnoteId(rawId);
277
+ if (!notes[id]) return match;
278
+ return '<sup class="footnote-ref"><a href="#fn' + id + '" id="fnref' + id + '">[' +
279
+ escapeHtml(rawId) + ']</a></sup>';
280
+ });
281
+
282
+ var items = order.map(function(id) {
283
+ var noteText = notes[id] || '';
284
+ var body = window.marked ? window.marked.parse(noteText) : '<p>' + escapeHtml(noteText) + '</p>';
285
+ return '<li id="fn' + id + '">' + body +
286
+ ' <a class="footnote-backref" href="#fnref' + id + '">↩</a></li>';
287
+ }).join('');
288
+ var footnotesHtml = '<div class="footnotes"><ol>' + items + '</ol></div>';
289
+ return replaced + footnotesHtml;
290
+ }
123
291
 
124
- if (window.renderMathInElement) {
125
- renderMathInElement(document.getElementById('content'), {
126
- delimiters: [
127
- {left: '$$', right: '$$', display: true},
128
- {left: '$', right: '$', display: false},
129
- {left: '\\(', right: '\\)', display: false},
130
- {left: '\\[', right: '\\]', display: true}
131
- ],
132
- throwOnError: false
292
+ function sanitizeFootnoteId(value) {
293
+ return String(value || '').replace(/[^a-zA-Z0-9_-]/g, '-');
294
+ }
295
+
296
+ function extractMathPlaceholders(text) {
297
+ var placeholders = {};
298
+ var out = [];
299
+ var idx = 0;
300
+ var inFence = false;
301
+ var fenceChar = '';
302
+ var fenceLen = 0;
303
+ var inlineDelimLen = 0;
304
+
305
+ function nextPlaceholder(value) {
306
+ var key = '@@MATH_' + Object.keys(placeholders).length + '@@';
307
+ placeholders[key] = value;
308
+ return key;
309
+ }
310
+
311
+ while (idx < text.length) {
312
+ var atLineStart = idx === 0 || text[idx - 1] === '\n';
313
+ if (inlineDelimLen === 0 && atLineStart) {
314
+ var lineEnd = text.indexOf('\n', idx);
315
+ if (lineEnd === -1) lineEnd = text.length;
316
+ var line = text.slice(idx, lineEnd);
317
+ var stripped = line.replace(/^[ ]+/, '');
318
+ var leadingSpaces = line.length - stripped.length;
319
+ if (leadingSpaces <= 3 && stripped) {
320
+ var first = stripped[0];
321
+ if (first === '`' || first === '~') {
322
+ var runLen = 0;
323
+ while (runLen < stripped.length && stripped[runLen] === first) {
324
+ runLen += 1;
325
+ }
326
+ if (runLen >= 3) {
327
+ if (!inFence) {
328
+ inFence = true;
329
+ fenceChar = first;
330
+ fenceLen = runLen;
331
+ } else if (first === fenceChar && runLen >= fenceLen) {
332
+ inFence = false;
333
+ fenceChar = '';
334
+ fenceLen = 0;
335
+ }
336
+ out.push(line);
337
+ idx = lineEnd;
338
+ continue;
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+ if (inFence) {
345
+ out.push(text[idx]);
346
+ idx += 1;
347
+ continue;
348
+ }
349
+
350
+ if (inlineDelimLen > 0) {
351
+ var delim = '`'.repeat(inlineDelimLen);
352
+ if (text.slice(idx, idx + inlineDelimLen) === delim) {
353
+ out.push(delim);
354
+ idx += inlineDelimLen;
355
+ inlineDelimLen = 0;
356
+ continue;
357
+ }
358
+ out.push(text[idx]);
359
+ idx += 1;
360
+ continue;
361
+ }
362
+
363
+ if (text[idx] === '`') {
364
+ var inlineRun = 0;
365
+ while (idx + inlineRun < text.length && text[idx + inlineRun] === '`') {
366
+ inlineRun += 1;
367
+ }
368
+ inlineDelimLen = inlineRun;
369
+ out.push('`'.repeat(inlineRun));
370
+ idx += inlineRun;
371
+ continue;
372
+ }
373
+
374
+ if (text.slice(idx, idx + 2) === '$$' && (idx === 0 || text[idx - 1] !== '\\')) {
375
+ var searchFrom = idx + 2;
376
+ var end = text.indexOf('$$', searchFrom);
377
+ while (end !== -1 && text[end - 1] === '\\') {
378
+ searchFrom = end + 2;
379
+ end = text.indexOf('$$', searchFrom);
380
+ }
381
+ if (end !== -1) {
382
+ out.push(nextPlaceholder(text.slice(idx, end + 2)));
383
+ idx = end + 2;
384
+ continue;
385
+ }
386
+ }
387
+
388
+ if (text[idx] === '$' && text.slice(idx, idx + 2) !== '$$' && (idx === 0 || text[idx - 1] !== '\\')) {
389
+ var lineEndInline = text.indexOf('\n', idx + 1);
390
+ if (lineEndInline === -1) lineEndInline = text.length;
391
+ var searchInline = idx + 1;
392
+ var endInline = text.indexOf('$', searchInline);
393
+ while (endInline !== -1 && endInline < lineEndInline && text[endInline - 1] === '\\') {
394
+ searchInline = endInline + 1;
395
+ endInline = text.indexOf('$', searchInline);
396
+ }
397
+ if (endInline !== -1 && endInline < lineEndInline) {
398
+ out.push(nextPlaceholder(text.slice(idx, endInline + 1)));
399
+ idx = endInline + 1;
400
+ continue;
401
+ }
402
+ }
403
+
404
+ out.push(text[idx]);
405
+ idx += 1;
406
+ }
407
+
408
+ return { text: out.join(''), placeholders: placeholders };
409
+ }
410
+
411
+ function replaceMathPlaceholders(html, placeholders) {
412
+ var out = String(html || '');
413
+ Object.keys(placeholders || {}).forEach(function(key) {
414
+ var value = escapeHtml(placeholders[key]);
415
+ out = out.split(key).join(value);
416
+ });
417
+ return out;
418
+ }
419
+
420
+ function runMarkdownEnhancements() {
421
+ var content = document.getElementById('content');
422
+ if (!content) return;
423
+
424
+ // Markmap: convert fenced markmap blocks to autoloader containers
425
+ var markmapBlocks = 0;
426
+ document.querySelectorAll('code.language-markmap').forEach(function(code) {
427
+ var pre = code.parentElement;
428
+ if (!pre) return;
429
+ var wrapper = document.createElement('div');
430
+ wrapper.className = 'markmap';
431
+ var template = document.createElement('script');
432
+ template.type = 'text/template';
433
+ template.textContent = code.textContent || '';
434
+ wrapper.appendChild(template);
435
+ pre.replaceWith(wrapper);
436
+ markmapBlocks += 1;
437
+ });
438
+ function resizeMarkmaps() {
439
+ document.querySelectorAll('.markmap svg').forEach(function(svg) {
440
+ try {
441
+ var bbox = svg.getBBox();
442
+ if (!bbox || !bbox.height) {
443
+ svg.style.height = '800px';
444
+ svg.style.width = '100%';
445
+ return;
446
+ }
447
+ var height = Math.ceil(bbox.height * 2);
448
+ svg.style.height = height + 'px';
449
+ if (bbox.width && bbox.width > svg.clientWidth) {
450
+ svg.style.width = Math.ceil(bbox.width * 2) + 'px';
451
+ if (svg.parentElement) {
452
+ svg.parentElement.style.overflowX = 'auto';
453
+ }
454
+ } else {
455
+ svg.style.width = '100%';
456
+ }
457
+ } catch (err) {
458
+ // Ignore sizing errors
459
+ }
460
+ });
461
+ }
462
+
463
+ if (markmapBlocks && window.markmap && window.markmap.autoLoader && window.markmap.autoLoader.renderAll) {
464
+ window.markmap.autoLoader.renderAll();
465
+ setTimeout(resizeMarkmaps, 120);
466
+ setTimeout(resizeMarkmaps, 600);
467
+ setTimeout(resizeMarkmaps, 1600);
468
+ if (!window.__markmapResizeBound) {
469
+ window.__markmapResizeBound = true;
470
+ window.addEventListener('resize', function() {
471
+ setTimeout(resizeMarkmaps, 120);
133
472
  });
134
473
  }
135
474
  }
136
475
 
137
- if (document.readyState === 'loading') {
138
- document.addEventListener('DOMContentLoaded', initRendering);
139
- } else {
140
- initRendering();
476
+ // Mermaid: convert fenced code blocks to mermaid divs
477
+ document.querySelectorAll('code.language-mermaid').forEach(function(code) {
478
+ var pre = code.parentElement;
479
+ var div = document.createElement('div');
480
+ div.className = 'mermaid';
481
+ div.textContent = code.textContent;
482
+ pre.replaceWith(div);
483
+ });
484
+
485
+ if (window.mermaid) {
486
+ mermaid.initialize({ startOnLoad: false });
487
+ mermaid.run();
488
+ }
489
+
490
+ if (window.renderMathInElement) {
491
+ renderMathInElement(content, {
492
+ delimiters: [
493
+ {left: '$$', right: '$$', display: true},
494
+ {left: '$', right: '$', display: false},
495
+ {left: '\\(', right: '\\)', display: false},
496
+ {left: '\\[', right: '\\]', display: true}
497
+ ],
498
+ throwOnError: false
499
+ });
141
500
  }
142
501
  }
143
502
 
503
+ function dispatchContentUpdated() {
504
+ var event;
505
+ try {
506
+ event = new CustomEvent('content:updated');
507
+ } catch (err) {
508
+ event = document.createEvent('Event');
509
+ event.initEvent('content:updated', true, true);
510
+ }
511
+ document.dispatchEvent(event);
512
+ }
513
+
144
514
  // ========================================
145
515
  // Footnote tooltips
146
516
  // ========================================
@@ -305,92 +675,125 @@
305
675
  var pdfUrl = document.body.dataset.pdfUrl;
306
676
  if (!pdfUrl) return;
307
677
 
308
- var pdfJsLib = window.pdfjsLib;
309
- if (!pdfJsLib) return;
310
-
311
- pdfJsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.js';
312
-
313
- var pdfDoc = null;
314
- var pageNum = 1;
315
- var pageRendering = false;
316
- var pageNumPending = null;
317
- var zoomLevel = 1.0;
318
- var canvas = document.getElementById('the-canvas');
319
- var ctx = canvas.getContext('2d');
320
-
321
- function renderPage(num) {
322
- pageRendering = true;
323
- pdfDoc.getPage(num).then(function(page) {
324
- var baseViewport = page.getViewport({scale: 1});
325
- var containerWidth = canvas.clientWidth || baseViewport.width;
326
- var fitScale = containerWidth / baseViewport.width;
327
- var scale = fitScale * zoomLevel;
328
-
329
- var viewport = page.getViewport({scale: scale});
330
- var outputScale = window.devicePixelRatio || 1;
331
-
332
- canvas.width = Math.floor(viewport.width * outputScale);
333
- canvas.height = Math.floor(viewport.height * outputScale);
334
- canvas.style.width = Math.floor(viewport.width) + 'px';
335
- canvas.style.height = Math.floor(viewport.height) + 'px';
336
-
337
- var transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
338
- var renderContext = { canvasContext: ctx, viewport: viewport, transform: transform };
339
- var renderTask = page.render(renderContext);
340
- renderTask.promise.then(function() {
341
- pageRendering = false;
342
- document.getElementById('page_num').textContent = String(pageNum);
343
- if (pageNumPending !== null) {
344
- var next = pageNumPending;
345
- pageNumPending = null;
346
- renderPage(next);
347
- }
678
+ var started = false;
679
+ var loadRequested = false;
680
+
681
+ function startPdf() {
682
+ if (started) return true;
683
+ var pdfJsLib = window.pdfjsLib;
684
+ if (!pdfJsLib || !pdfJsLib.getDocument) return false;
685
+
686
+ started = true;
687
+ pdfJsLib.GlobalWorkerOptions.workerSrc =
688
+ window.PDFJS_WORKER_SRC || '/pdfjs/build/pdf.worker.js';
689
+
690
+ var pdfDoc = null;
691
+ var pageNum = 1;
692
+ var pageRendering = false;
693
+ var pageNumPending = null;
694
+ var zoomLevel = 1.0;
695
+ var canvas = document.getElementById('the-canvas');
696
+ var ctx = canvas.getContext('2d');
697
+
698
+ function renderPage(num) {
699
+ pageRendering = true;
700
+ pdfDoc.getPage(num).then(function(page) {
701
+ var baseViewport = page.getViewport({scale: 1});
702
+ var containerWidth = canvas.clientWidth || baseViewport.width;
703
+ var fitScale = containerWidth / baseViewport.width;
704
+ var scale = fitScale * zoomLevel;
705
+
706
+ var viewport = page.getViewport({scale: scale});
707
+ var outputScale = window.devicePixelRatio || 1;
708
+
709
+ canvas.width = Math.floor(viewport.width * outputScale);
710
+ canvas.height = Math.floor(viewport.height * outputScale);
711
+ canvas.style.width = Math.floor(viewport.width) + 'px';
712
+ canvas.style.height = Math.floor(viewport.height) + 'px';
713
+
714
+ var transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
715
+ var renderContext = { canvasContext: ctx, viewport: viewport, transform: transform };
716
+ var renderTask = page.render(renderContext);
717
+ renderTask.promise.then(function() {
718
+ pageRendering = false;
719
+ document.getElementById('page_num').textContent = String(pageNum);
720
+ if (pageNumPending !== null) {
721
+ var next = pageNumPending;
722
+ pageNumPending = null;
723
+ renderPage(next);
724
+ }
725
+ });
348
726
  });
349
- });
350
- }
727
+ }
351
728
 
352
- function queueRenderPage(num) {
353
- if (pageRendering) {
354
- pageNumPending = num;
355
- } else {
356
- renderPage(num);
729
+ function queueRenderPage(num) {
730
+ if (pageRendering) {
731
+ pageNumPending = num;
732
+ } else {
733
+ renderPage(num);
734
+ }
357
735
  }
358
- }
359
736
 
360
- function onPrevPage() {
361
- if (pageNum <= 1) return;
362
- pageNum--;
363
- queueRenderPage(pageNum);
364
- }
737
+ function onPrevPage() {
738
+ if (pageNum <= 1) return;
739
+ pageNum--;
740
+ queueRenderPage(pageNum);
741
+ }
365
742
 
366
- function onNextPage() {
367
- if (pageNum >= pdfDoc.numPages) return;
368
- pageNum++;
369
- queueRenderPage(pageNum);
370
- }
743
+ function onNextPage() {
744
+ if (pageNum >= pdfDoc.numPages) return;
745
+ pageNum++;
746
+ queueRenderPage(pageNum);
747
+ }
371
748
 
372
- function adjustZoom(delta) {
373
- zoomLevel = Math.max(0.5, Math.min(3.0, zoomLevel + delta));
374
- queueRenderPage(pageNum);
375
- }
749
+ function adjustZoom(delta) {
750
+ zoomLevel = Math.max(0.5, Math.min(3.0, zoomLevel + delta));
751
+ queueRenderPage(pageNum);
752
+ }
376
753
 
377
- document.getElementById('prev').addEventListener('click', onPrevPage);
378
- document.getElementById('next').addEventListener('click', onNextPage);
379
- document.getElementById('zoomOut').addEventListener('click', function() { adjustZoom(-0.1); });
380
- document.getElementById('zoomIn').addEventListener('click', function() { adjustZoom(0.1); });
754
+ document.getElementById('prev').addEventListener('click', onPrevPage);
755
+ document.getElementById('next').addEventListener('click', onNextPage);
756
+ document.getElementById('zoomOut').addEventListener('click', function() { adjustZoom(-0.1); });
757
+ document.getElementById('zoomIn').addEventListener('click', function() { adjustZoom(0.1); });
381
758
 
382
- pdfJsLib.getDocument(pdfUrl).promise.then(function(pdfDoc_) {
383
- pdfDoc = pdfDoc_;
384
- document.getElementById('page_count').textContent = String(pdfDoc.numPages);
385
- renderPage(pageNum);
386
- });
759
+ pdfJsLib.getDocument(pdfUrl).promise.then(function(pdfDoc_) {
760
+ pdfDoc = pdfDoc_;
761
+ document.getElementById('page_count').textContent = String(pdfDoc.numPages);
762
+ renderPage(pageNum);
763
+ });
387
764
 
388
- var resizeTimer = null;
389
- window.addEventListener('resize', function() {
390
- if (!pdfDoc) return;
391
- if (resizeTimer) clearTimeout(resizeTimer);
392
- resizeTimer = setTimeout(function() { queueRenderPage(pageNum); }, 150);
393
- });
765
+ var resizeTimer = null;
766
+ window.addEventListener('resize', function() {
767
+ if (!pdfDoc) return;
768
+ if (resizeTimer) clearTimeout(resizeTimer);
769
+ resizeTimer = setTimeout(function() { queueRenderPage(pageNum); }, 150);
770
+ });
771
+
772
+ return true;
773
+ }
774
+
775
+ if (startPdf()) return;
776
+
777
+ if (!loadRequested && window.PDFJS_SCRIPT_SRC) {
778
+ loadRequested = true;
779
+ var script = document.createElement('script');
780
+ script.src = window.PDFJS_SCRIPT_SRC;
781
+ script.defer = true;
782
+ script.onload = function() {
783
+ try {
784
+ window.dispatchEvent(new Event('pdfjs:loaded'));
785
+ } catch (err) {
786
+ var event = document.createEvent('Event');
787
+ event.initEvent('pdfjs:loaded', true, true);
788
+ window.dispatchEvent(event);
789
+ }
790
+ };
791
+ document.head.appendChild(script);
792
+ }
793
+
794
+ window.addEventListener('pdfjs:loaded', function() {
795
+ startPdf();
796
+ }, { once: true });
394
797
  }
395
798
 
396
799
  // ========================================