Your IP : 3.142.201.19
import {Text, Uri, Loc, Dom, Reflection, Event, Runtime, ajax as Ajax, Type} from 'main.core';
import {EventEmitter} from 'main.core.events';
const Item = Reflection.namespace('BX.UI.Viewer.Item');
const Util = Reflection.namespace('BX.util');
const BXPromise = Reflection.namespace('BX.Promise');
const DEFAULT_SCALE = 1.4;
const PAGES_TO_PRELOAD = 3;
// noinspection JSClosureCompilerSyntax
/**
* @memberof BX.UI.Viewer
* @extends BX.UI.Viewer.Item
*/
export class Document extends Item
{
static #loadingLibraryPromise = null;
#pageNumber: number = 1;
#loadingDocumentPromise: Promise = null;
pdfDocument;
pdfPages: Object<number,Object> = {};
scale: number = DEFAULT_SCALE;
pdfRenderedPages: Object<number,Object> = {};
lastRenderedPdfPage: number = 0;
contentNode: Element;
previewHtml: Element;
disableAnnotationLayer: boolean = false;
constructor (options)
{
super(options);
options = options || {};
this.scale = options.scale || DEFAULT_SCALE;
}
setPropertiesByNode(node:HTMLElement): void
{
super.setPropertiesByNode(node);
this.disableAnnotationLayer = node.dataset.hasOwnProperty('disableAnnotationLayer');
}
applyReloadOptions(options)
{
this.controller.unsetCachedData(this.src);
}
listContainerModifiers(): Array<string>
{
const result = [
'ui-viewer-document',
];
if (this.controller.stretch)
{
result.push('--stretch');
}
return result;
}
setSrc(src: string|Uri): this
{
this.src = src;
this._pdfSrc = null;
return this.#resetState();
}
setPdfSource(pdfSource: string|Uri|ArrayBuffer): this
{
this._pdfSrc = pdfSource;
return this.#resetState();
}
#resetState(): this
{
this.pdfRenderedPages = {};
this.lastRenderedPdfPage = null;
this.pdfDocument = null;
this.pdfPages = {};
this.setPageNumber(1);
if (this.printer)
{
this.hidePrintProgress();
this.printer.destroy();
}
}
loadLibrary(): Promise
{
if (Document.#loadingLibraryPromise !== null)
{
return Document.#loadingLibraryPromise;
}
Document.#loadingLibraryPromise = new Promise((resolve, reject) => {
Runtime.loadExtension('ui.pdfjs').then(() => {
if (!pdfjsLib.GlobalWorkerOptions.workerSrc)
{
pdfjsLib.GlobalWorkerOptions.workerSrc = '/bitrix/js/ui/pdfjs/pdf.worker.js';
}
Document.#loadingLibraryPromise = null;
resolve();
})
.catch(reject);
});
return Document.#loadingLibraryPromise;
}
loadData(): BXPromise
{
const promise = new BXPromise();
if (this._pdfSrc)
{
this.loadLibrary().then(() => {
promise.fulfill(this);
});
return promise;
}
console.log('loadData pdf');
const ajaxPromise = Ajax.promise({
url: Uri.addParam(this.src, {ts: 'bxviewer'}),
method: 'GET',
dataType: 'json',
headers: [
{
name: 'BX-Viewer-src',
value: this.src
},
{
name: 'BX-Viewer',
value: 'document'
}
]
});
ajaxPromise.then((response) => {
if (!response || !response.data)
{
this.isTransforming = false;
promise.reject({
item: this,
message: Loc.getMessage("JS_UI_VIEWER_ITEM_TRANSFORMATION_ERROR_1").replace('#DOWNLOAD_LINK#', this.getSrc()),
type: 'error'
});
return promise;
}
if (response.data.hasOwnProperty('pullTag'))
{
if (!this.isTransforming)
{
this.transformationPromise = promise;
this.registerTransformationHandler(response.data.pullTag);
}
this.isTransforming = true;
}
if (response.data.data && response.data.data.src)
{
this.isTransforming = false;
this._pdfSrc = response.data.data.src;
this.loadLibrary().then(() => {
promise.fulfill(this);
});
}
});
return promise;
}
render(): HTMLDivElement
{
this.controller.showLoading();
this.contentNode = Dom.create('div', {
props: {
className: 'ui-viewer-item-document-content',
tabIndex: 2208
},
});
Event.bind(this.contentNode, 'scroll', Runtime.throttle(this.handleScrollDocument.bind(this), 100));
return this.contentNode;
}
getNakedActions(): Array
{
const nakedActions = super.getNakedActions();
return this.insertPrintBeforeInfo(nakedActions);
}
insertPrintBeforeInfo(actions: Array): Array<{type: string, action: Function}>
{
actions = actions || [];
let infoIndex = null;
for (let i = 0; i < actions.length; i++)
{
if (actions[i].type === 'info')
{
infoIndex = i;
}
}
const printAction = {
type: 'print',
action: this.print.bind(this)
};
if (infoIndex === null)
{
actions.push(printAction);
}
else
{
actions = Util.insertIntoArray(actions, infoIndex, printAction);
}
return actions;
}
getFirstDocumentPageHeight(): Promise<number>
{
if (this._height)
{
return Promise.resolve(this._height);
}
return new Promise((resolve) => {
this.getDocumentPage(this.pdfDocument, 1).then((page) => {
const viewport = page.getViewport(this.scale);
this._height = viewport.height;
resolve(this._height);
});
});
}
handleScrollDocument(event): void
{
this.getFirstDocumentPageHeight().then((height) => {
const scrollBottom = this.contentNode.scrollHeight - this.contentNode.scrollTop - this.contentNode.clientHeight;
if (scrollBottom < height * PAGES_TO_PRELOAD && this.pdfDocument.numPages > this.lastRenderedPdfPage)
{
for (let i = this.lastRenderedPdfPage + 1; i <= Math.min(this.pdfDocument.numPages, this.lastRenderedPdfPage + PAGES_TO_PRELOAD); i++)
{
this.renderDocumentPage(this.pdfDocument, i);
}
}
this.setPageNumber((this.contentNode.scrollTop / height) + 1);
});
}
loadDocument(): Promise<Object>
{
if (this.pdfDocument)
{
return Promise.resolve(this.pdfDocument);
}
if (this.#loadingDocumentPromise)
{
return this.#loadingDocumentPromise;
}
this.#loadingDocumentPromise = new Promise((resolve) => {
this.loadData().then(() => {
pdfjsLib.getDocument(this._pdfSrc).promise.then((pdf) => {
this.pdfDocument = pdf;
this.#loadingDocumentPromise = null;
resolve(this.pdfDocument);
});
});
});
return this.#loadingDocumentPromise;
}
getDocumentPage(pdf, pageNumber): Promise<Object>
{
if (this.pdfPages[pageNumber])
{
return Promise.resolve(this.pdfPages[pageNumber]);
}
return new Promise((resolve) => {
pdf.getPage(pageNumber).then((page) => {
this.pdfPages[pageNumber] = page;
resolve(this.pdfPages[pageNumber]);
});
});
}
renderDocumentPage(pdf, pageNumber): Promise<Object>
{
const pagePromise = this.pdfRenderedPages[pageNumber];
if (pagePromise instanceof Promise)
{
return pagePromise;
}
else if(!!pagePromise)
{
return Promise.resolve(pagePromise);
}
this.pdfRenderedPages[pageNumber] = new Promise((resolve) => {
this.getDocumentPage(pdf, pageNumber).then((page) => {
const canvas = this.createCanvasPage();
const viewport = page.getViewport(this.scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderPromise = page.render({canvasContext: canvas.getContext('2d'), viewport: viewport});
if (!this.disableAnnotationLayer)
{
renderPromise.then(function () {
return page.getAnnotations();
}).then(function (annotationData) {
const annotationLayer = Dom.create('div', {
props: {className: 'ui-viewer-pdf-annotation-layer'}
});
Dom.insertAfter(annotationLayer, canvas);
Dom.adjust(annotationLayer, {
style: {
margin: '-' + canvas.offsetHeight + 'px auto 0 auto',
height: canvas.height + 'px',
width: canvas.width + 'px'
}
});
pdfjsLib.AnnotationLayer.render({
viewport: viewport.clone({dontFlip: true}),
linkService: pdfjsLib.SimpleLinkService,
div: annotationLayer,
annotations: annotationData,
page: page
});
});
}
renderPromise.then(function () {
return page.getTextContent();
}).then(function (textContent) {
const textLayer = Dom.create('div', {
props: {className: 'ui-viewer-pdf-text-layer'}
});
Dom.insertAfter(textLayer, canvas);
Dom.adjust(textLayer, {
style: {
margin: '-' + canvas.offsetHeight + 'px auto 0 auto',
height: canvas.height + 'px',
width: canvas.width + 'px'
}
});
pdfjsLib.renderTextLayer({
textContent: textContent,
container: textLayer,
viewport: viewport,
textDivs: []
});
});
this.lastRenderedPdfPage = Math.max(pageNumber, this.lastRenderedPdfPage);
if (pageNumber === 1)
{
this.firstWidthDocumentPage = canvas.width;
}
renderPromise.then(() => {
this.controller.hideLoading();
this.pdfRenderedPages[pageNumber] = page;
resolve(page, canvas);
});
});
});
return this.pdfRenderedPages[pageNumber];
}
createCanvasPage(): HTMLCanvasElement
{
const canvas = document.createElement('canvas');
canvas.className = 'ui-viewer-document-page-canvas';
this.contentNode.appendChild(canvas);
return canvas;
}
getContentWidth (): Promise<Number>
{
if (this.firstWidthDocumentPage)
{
return Promise.resolve(this.firstWidthDocumentPage);
}
return new Promise((resolve) => {
this.loadDocument().then(() => {
this.renderDocumentPage(this.pdfDocument, 1).then((page) => {
resolve(page.getViewport(this.scale).width);
})
});
});
}
afterRender(): void
{
this.loadDocument().then((pdf) => {
for (let i = 1; i <= Math.min(pdf.numPages, PAGES_TO_PRELOAD); i++)
{
if (i === 1)
{
this._handleControls = this.controller.handleVisibleControls.bind(this.controller);
this.controller.enableReadingMode(true);
const printAction = this.controller.actionPanel.getItemById('print');
if (printAction)
{
printAction.layout.container.classList.remove('ui-btn-disabled');
}
Runtime.throttle(Event.bind(window, 'mousemove', this._handleControls), 20);
}
this.renderDocumentPage(pdf, i);
}
});
}
beforeHide(): void
{
this.pdfRenderedPages = {};
Event.unbind(window, 'mousemove', this._handleControls);
if (this.printer)
{
this.hidePrintProgress();
this.printer.destroy();
}
}
updatePrintProgressMessage(index: number, total: number): void
{
const progress = Math.round((index / total) * 100);
this.controller.setTextOnLoading(Loc.getMessage('JS_UI_VIEWER_ITEM_PREPARING_TO_PRINT').replace('#PROGRESS#', progress));
}
showPrintProgress(index: number, total: number): void
{
this.contentNode.style.opacity = 0.7;
this.contentNode.style.filter = 'blur(2px)';
this.controller.showLoading({
zIndex: 1
});
this.updatePrintProgressMessage(index, total);
}
hidePrintProgress(): void
{
this.contentNode.style.opacity = null;
this.contentNode.style.filter = null;
this.controller.hideLoading();
}
print(): void
{
if (!this.pdfDocument)
{
console.warn('Where is pdf document to print?');
return;
}
this.showPrintProgress(0, this.pdfDocument.numPages);
this.printer = new PrintService({
pdf: this.pdfDocument
});
this.printer.init().then(() => {
this.printer.prepare({
onProgress: this.updatePrintProgressMessage.bind(this)
}).then(() => {
this.hidePrintProgress();
this.printer.performPrint();
});
});
}
handleKeyPress(event): void
{
switch (event.code)
{
case 'PageDown':
case 'PageUp':
case 'ArrowDown':
case 'ArrowUp':
BX.focus(this.contentNode);
break;
}
}
getScale(): number
{
return this.scale;
}
setScale(scale: number): this
{
this.scale = scale;
return this;
}
updateScale(scale: number): Promise<void>
{
scale = Number(scale);
if (this.scale === scale)
{
return Promise.resolve();
}
const ratio = scale / this.scale;
const updatePageScale = ((
page,
canvases: Array<number, HTMLCanvasElement>,
textLayers: Array<number,HTMLDivElement>
): Promise => {
const canvas = canvases[page.pageIndex];
if (!canvas)
{
return Promise.resolve();
}
return new Promise((resolve) => {
const viewport = page.getViewport(this.scale);
canvas.width = viewport.width;
canvas.height = viewport.height;
page.render({
canvasContext: canvas.getContext('2d'),
viewport: viewport,
}).then(() => {
const textLayer = textLayers[page.pageIndex];
if (textLayer)
{
Dom.clean(textLayer);
Dom.adjust(textLayer, {
style: {
margin: '-' + canvas.offsetHeight + 'px auto 0 auto',
height: viewport.height + 'px',
width: viewport.width + 'px'
}
});
page.getTextContent().then((textContent) => {
pdfjsLib.renderTextLayer({
textContent: textContent,
container: textLayer,
viewport: viewport,
textDivs: []
});
resolve();
});
}
else
{
resolve();
}
});
});
});
const promises = [];
this.scale = scale;
const canvases = Array.from(this.contentNode.querySelectorAll('canvas[class="ui-viewer-document-page-canvas"]'));
const textLayers = Array.from(this.contentNode.querySelectorAll('div[class="ui-viewer-pdf-text-layer"]'));
Object.values(this.pdfRenderedPages).forEach((renderedPage) => {
if (renderedPage instanceof Promise)
{
promises.push(new Promise((resolve) => {
renderedPage.then((page) => {
updatePageScale(page, canvases, textLayers).then(resolve);
});
}));
}
else
{
promises.push(updatePageScale(renderedPage, canvases, textLayers));
}
});
const scrollTop = this.contentNode.scrollTop * ratio;
this.contentNode.scrollTo(this.contentNode.scrollLeft, scrollTop);
return Promise.all(promises);
}
getPagesNumber(): ?number
{
if (!this.pdfDocument)
{
return null;
}
return Text.toInteger(this.pdfDocument._pdfInfo.numPages);
}
scrollToPage(pageNumber: number): Promise<void>
{
const isChanged = this.setPageNumber(pageNumber) !== null;
if (!isChanged)
{
return Promise.resolve();
}
return new Promise((resolve) => {
const renderPromises = [];
for (let i = 1; i < pageNumber; i++)
{
renderPromises.push(this.renderDocumentPage(this.pdfDocument, i));
}
Promise.all(renderPromises).then((pages) => {
let height = 0;
pages.forEach((page) => {
const viewport = page.getViewport(this.scale);
height += viewport.height + 7;
});
this.contentNode.scrollTo(this.contentNode.scrollLeft, height);
resolve();
});
});
}
getPageNumber(): number
{
return this.#pageNumber;
}
setPageNumber(pageNumber: number): this|null
{
pageNumber = Text.toInteger(pageNumber);
if (pageNumber < 0)
{
pageNumber = 1;
}
let numPages = this.getPagesNumber();
if (!numPages)
{
numPages = 1;
}
if (pageNumber > numPages)
{
pageNumber = numPages;
}
if (this.#pageNumber !== pageNumber)
{
this.#pageNumber = pageNumber;
EventEmitter.emit(this, 'BX.UI.Viewer.Item.Document:updatePageNumber');
return this;
}
return null;
}
}
const PRINT_SCALE = 1;
export class PrintService
{
constructor(options)
{
options = options || {};
this.pdf = options.pdf;
this.iframe = null;
this.documentOverview = {};
}
init()
{
if (this.documentOverview)
{
return Promise.resolve(this.documentOverview);
}
return new Promise((resolve) => {
this.pdf.getPage(1).then((page) => {
const viewport = page.getViewport(PRINT_SCALE);
this.documentOverview = {
width: viewport.width, height: viewport.height, rotation: viewport.rotation
};
resolve(this.documentOverview);
});
});
}
/**
* @param {?Object} options
* @param {Function} [options.onProgress]
* @return {BXPromise}
*/
prepare(options)
{
options = options || {};
const pageCount = this.pdf.numPages;
let currentPage = -1;
const promise = new BXPromise();
let onProgress = null;
if (Type.isFunction(options.onProgress))
{
onProgress = options.onProgress;
}
this.frame = this.createIframe();
const process = () => {
if (++currentPage >= pageCount)
{
console.log('finish', this.frame.contentWindow.document);
setTimeout(() => {
promise.fulfill();
}, 1000);
return;
}
this.renderPage(currentPage + 1).then(function () {
if (onProgress)
{
onProgress(currentPage + 1, pageCount);
}
process();
});
};
process();
return promise;
}
renderPage(pageNumber)
{
return this.pdf.getPage(pageNumber).then(function (page) {
const scratchCanvas = document.createElement('canvas');
const viewport = page.getViewport(1);
// The size of the canvas in pixels for printing.
const PRINT_RESOLUTION = 150;
const PRINT_UNITS = PRINT_RESOLUTION / 72.0;
scratchCanvas.width = Math.floor(viewport.width * PRINT_UNITS);
scratchCanvas.height = Math.floor(viewport.height * PRINT_UNITS);
// The physical size of the img as specified by the PDF document.
const CSS_UNITS = 96.0 / 72.0;
const width = Math.floor(viewport.width * CSS_UNITS) + 'px';
const height = Math.floor(viewport.height * CSS_UNITS) + 'px';
const ctx = scratchCanvas.getContext('2d');
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
ctx.restore();
const renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: page.getViewport(1, viewport.rotation),
intent: 'print'
};
return page.render(renderContext).promise.then(function () {
return {
scratchCanvas: scratchCanvas, width: width, height: height
}
});
}).then((printItem) => {
const img = document.createElement('img');
img.style.width = printItem.width;
img.style.height = printItem.height;
const scratchCanvas = printItem.scratchCanvas;
if (('toBlob' in scratchCanvas) && !this.disableCreateObjectURL)
{
scratchCanvas.toBlob(function (blob) {
img.src = URL.createObjectURL(blob);
});
}
else
{
img.src = scratchCanvas.toDataURL();
}
const wrapper = document.createElement('div');
wrapper.appendChild(img);
this.frame.contentWindow.document.body.appendChild(wrapper);
});
}
destroy()
{
if (this.frame)
{
Dom.remove(this.frame);
}
}
createIframe()
{
const frame = document.createElement("iframe");
frame.src = "about:blank";
frame.name = "document-print-frame";
frame.style.display = "none";
document.body.appendChild(frame);
const frameWindow = frame.contentWindow;
const frameDoc = frameWindow.document;
frameDoc.open();
frameDoc.write('<html><head>');
const pageSize = this.getDocumentOverview();
let headTags = "<style>";
headTags += "html, body { background: #fff !important; height: 100%; }";
headTags += '@supports ((size:A4) and (size:1pt 1pt)) {' + '@page { size: ' + pageSize.width + 'pt ' + pageSize.height + 'pt;}' + '}';
headTags += '#ad{ display:none;}';
headTags += '#leftbar{ display:none;}';
headTags += "</style>";
frameDoc.write(headTags);
frameDoc.write('</head><body>');
frameDoc.write('</body></html>');
frameDoc.close();
return frame;
}
performPrint()
{
this.frame.contentWindow.focus();
this.frame.contentWindow.print();
}
getDocumentOverview()
{
return this.documentOverview;
}
}