ngx-doc-viewer
Version:
Angular document viewer.
407 lines (400 loc) • 17.1 kB
JavaScript
import { __awaiter } from 'tslib';
import { EventEmitter, Component, NgZone, Output, Input, ViewChildren, NgModule } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
const fileToArray = (url) => {
return new Promise((resolve, reject) => {
try {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = () => {
const reader = new FileReader();
reader.readAsArrayBuffer(request.response);
reader.onloadend = () => {
resolve(reader.result);
};
};
request.send();
}
catch (_a) {
reject(`error while retrieving file ${url}.`);
}
});
};
const reloadIFrame = (iframe) => {
if (iframe) {
console.log('reloading..');
// eslint-disable-next-line no-self-assign
iframe.src = iframe.src;
}
};
const ɵ0$1 = reloadIFrame;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleFileUpload = (fileInput) => {
return new Promise((resolve, reject) => {
if (fileInput.target.files && fileInput.target.files[0]) {
const reader = new FileReader();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
reader.onload = (e) => {
resolve(e.target.result);
};
reader.readAsDataURL(fileInput.target.files[0]);
}
else {
reject('no files selected');
}
});
};
const getbaseUrl = () => {
const pathArray = window.location.href.split('/');
const protocol = pathArray[0];
const host = pathArray[2];
return protocol + '//' + host;
};
const getLocation = (href) => {
const match = href.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);
return match && {
href,
protocol: match[1],
host: match[2],
hostname: match[3],
port: match[4],
pathname: match[5],
search: match[6],
hash: match[7]
};
};
const getDocxToHtml = (url) => __awaiter(void 0, void 0, void 0, function* () {
if (!mammoth) {
console.error('Please install mammoth and make sure mammoth.browser.min.js is loaded.');
}
const arrayBuffer = yield fileToArray(url);
const resultObject = yield mammoth.convertToHtml({ arrayBuffer });
return resultObject.value;
});
const googleCheckSubscription = () => {
let subscription = null;
let checkCount = 0;
return {
subscribe: (iframe, interval = 3000, maxChecks = 5) => {
if (!iframeIsLoaded(iframe)) {
subscription = setInterval(() => {
checkCount++;
if (checkCount >= maxChecks) {
clearInterval(subscription);
}
reloadIFrame(iframe);
}, interval);
return subscription;
}
else {
if (subscription) {
clearInterval(subscription);
}
}
},
unsubscribe: () => {
if (subscription) {
clearInterval(subscription);
}
},
};
};
const iframeIsLoaded = (iframe) => {
var _a;
// its #document <html><head></head><body></body></html> when google is returning a 204
// so if contentDocument = null then it's loaded.
let isLoaded = false;
try {
if (!internetExplorer()) {
isLoaded = !(iframe === null || iframe === void 0 ? void 0 : iframe.contentDocument);
}
else {
isLoaded = !((_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document);
}
}
catch (_b) {
// ignore message Blocked a frame with origin "http://..." from accessing a cross-origin frame.
}
return isLoaded;
};
const internetExplorer = () => (/MSIE (\d+\.\d+);/.test(navigator.userAgent) || navigator.userAgent.indexOf("Trident/") > -1);
const ɵ1 = internetExplorer;
const getViewerDetails = (url, configuredViewer = 'google', queryParams = '', viewerUrl = '') => {
switch (configuredViewer) {
case 'google':
viewerUrl = `https://docs.google.com/gview?url=%URL%&embedded=true`;
break;
case 'office': {
viewerUrl = `https://view.officeapps.live.com/op/embed.aspx?src=%URL%`;
break;
}
case 'pdf': {
viewerUrl = '';
break;
}
}
const externalViewer = configuredViewer === 'google' ||
configuredViewer === 'office' ||
configuredViewer === 'url';
const u = url.indexOf('/') ? encodeURIComponent(url) : url;
let fullUrl = viewerUrl ? viewerUrl.replace('%URL%', u) : url;
if (queryParams && externalViewer && configuredViewer !== 'url') {
const start = queryParams.startsWith('&') ? '' : '&';
fullUrl = `${fullUrl}${start}${queryParams}`;
}
return {
url: fullUrl,
externalViewer,
};
};
const replaceLocalUrl = (url, overrideLocalhost) => {
const loc = getLocation(url);
const locReplace = getLocation(overrideLocalhost);
if (loc && locReplace) {
return url.replace(loc.port ? `${loc.hostname}:${loc.port}` : loc.hostname, locReplace.port ? `${locReplace.hostname}:${locReplace.port}` : locReplace.hostname);
}
return url;
};
const getBlobFromUrl = (url) => {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = () => {
resolve(request.response);
};
request.onerror = reject;
request.send();
});
};
const ɵ2 = getBlobFromUrl;
const uploadToCloud = (fileUrl, api) => new Promise((resolve, reject) => {
getBlobFromUrl(fileUrl).then(blob => {
var _a, _b;
const loc = getLocation(fileUrl);
const name = (loc === null || loc === void 0 ? void 0 : loc.pathname) ? (_a = loc === null || loc === void 0 ? void 0 : loc.pathname) === null || _a === void 0 ? void 0 : _a.split('/')[((_b = loc === null || loc === void 0 ? void 0 : loc.pathname) === null || _b === void 0 ? void 0 : _b.split('/').length) - 1] : '';
const formData = new FormData();
const request = new XMLHttpRequest();
formData.append('file', blob, name);
request.onreadystatechange = e => {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status === 200) {
resolve(request.responseText);
}
else {
reject(request.responseText);
}
}
};
request.onerror = reject;
request.open('post', api, true);
request.send(formData);
});
});
const isLocalFile = (file) => {
const loc = getLocation(file);
const hostname = (loc === null || loc === void 0 ? void 0 : loc.hostname) || '';
return ((['localhost', '127.0.0.1', '', '::1'].includes(hostname))
|| (hostname.startsWith('192.168.'))
|| (hostname.startsWith('10.0.'))
|| (hostname.endsWith('.local')));
};
class NgxDocViewerComponent {
constructor(domSanitizer, ngZone) {
this.domSanitizer = domSanitizer;
this.ngZone = ngZone;
this.loaded = new EventEmitter();
this.url = '';
this.queryParams = '';
this.viewerUrl = '';
this.googleCheckInterval = 3000;
this.googleMaxChecks = 5;
this.disableContent = 'none';
this.googleCheckContentLoaded = true;
this.fullUrl = null;
this.externalViewer = false;
this.docHtml = '';
this.configuredViewer = 'google';
this.shouldCheckIframe = false;
}
ngAfterViewInit() {
var _a, _b;
if (this.shouldCheckIframe) {
const iframe = (_b = (_a = this.iframes) === null || _a === void 0 ? void 0 : _a.first) === null || _b === void 0 ? void 0 : _b.nativeElement;
if (iframe) {
this.shouldCheckIframe = false;
this.reloadIframe(iframe);
}
}
}
ngOnDestroy() {
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
}
ngOnChanges(changes) {
return __awaiter(this, void 0, void 0, function* () {
if (changes &&
changes.viewer &&
(changes.viewer.isFirstChange ||
changes.viewer.currentValue !== changes.viewer.previousValue)) {
if (this.viewer !== 'google' &&
this.viewer !== 'office' &&
this.viewer !== 'mammoth' &&
this.viewer !== 'pdf' &&
this.viewer !== 'url') {
console.error(`Unsupported viewer: '${this.viewer}'. Supported viewers: google, office, mammoth and pdf`);
}
this.configuredViewer = this.viewer;
}
if ((changes.url && changes.url.currentValue !== changes.url.previousValue) ||
(changes.viewer &&
changes.viewer.currentValue !== changes.viewer.previousValue) ||
(changes.viewerUrl &&
changes.viewerUrl.currentValue !== changes.viewerUrl.previousValue)) {
let viewerDetails = getViewerDetails(this.url, this.configuredViewer, this.queryParams, this.viewerUrl);
this.externalViewer = viewerDetails.externalViewer;
if (viewerDetails.externalViewer && this.overrideLocalhost && isLocalFile(this.url)) {
const newUrl = replaceLocalUrl(this.url, this.overrideLocalhost);
viewerDetails = getViewerDetails(newUrl, this.configuredViewer, this.queryParams, this.viewerUrl);
}
this.docHtml = '';
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
if (!this.url) {
this.fullUrl = null;
}
else if (viewerDetails.externalViewer ||
this.configuredViewer === 'url') {
this.fullUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(viewerDetails.url);
// see:
// https://stackoverflow.com/questions/40414039/google-docs-viewer-returning-204-responses-no-longer-working-alternatives
// hack to reload iframe if it's not loaded.
// would maybe be better to use view.officeapps.live.com but seems not to work with sas token.
if (this.configuredViewer === 'google' &&
this.googleCheckContentLoaded) {
this.ngZone.runOutsideAngular(() => {
var _a, _b;
// if it's not loaded after the googleIntervalCheck, then open load again.
const iframe = (_b = (_a = this.iframes) === null || _a === void 0 ? void 0 : _a.first) === null || _b === void 0 ? void 0 : _b.nativeElement;
if (iframe) {
this.reloadIframe(iframe);
}
else {
this.shouldCheckIframe = true;
}
});
}
}
else if (this.configuredViewer === 'mammoth') {
this.docHtml = yield getDocxToHtml(this.url);
}
}
});
}
reloadIframe(iframe) {
this.checkIFrameSubscription = googleCheckSubscription();
this.checkIFrameSubscription.subscribe(iframe, this.googleCheckInterval, this.googleMaxChecks);
}
iframeLoaded() {
var _a, _b;
const iframe = (_b = (_a = this.iframes) === null || _a === void 0 ? void 0 : _a.first) === null || _b === void 0 ? void 0 : _b.nativeElement;
if (iframe && iframeIsLoaded(iframe)) {
this.loaded.emit(null);
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
}
}
}
NgxDocViewerComponent.decorators = [
{ type: Component, args: [{
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'ngx-doc-viewer',
template: "<ng-container *ngIf=\"!externalViewer\">\r\n <div *ngIf=\"configuredViewer !== 'pdf'\" [innerHtml]=\"docHtml\"></div>\r\n <object\r\n *ngIf=\"fullUrl && configuredViewer === 'pdf'\"\r\n [data]=\"fullUrl\"\r\n type=\"application/pdf\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n >\r\n <p>\r\n Your browser does not support PDFs.\r\n <a [href]=\"fullUrl\">Download the PDF</a>.\r\n </p>\r\n </object>\r\n</ng-container>\r\n<ng-container *ngIf=\"externalViewer\">\r\n <iframe\r\n (load)=\"iframeLoaded()\"\r\n *ngIf=\"fullUrl && disableContent === 'none'\"\r\n #iframe\r\n id=\"iframe-doc-viewer\"\r\n frameBorder=\"0\"\r\n [src]=\"fullUrl\"\r\n ></iframe>\r\n <div class=\"container\" *ngIf=\"disableContent !== 'none'\">\r\n <div\r\n [class.overlay-full]=\"disableContent === 'all'\"\r\n [class.overlay-popout-google]=\"\r\n configuredViewer === 'google' &&\r\n (disableContent === 'popout' || disableContent === 'popout-hide')\r\n \"\r\n [class.overlay-popout-office]=\"\r\n configuredViewer === 'office' &&\r\n (disableContent === 'popout' || disableContent === 'popout-hide')\r\n \"\r\n [style.background-color]=\"\r\n disableContent === 'popout-hide' ? '#fff' : 'transparent'\r\n \"\r\n ></div>\r\n <iframe\r\n (load)=\"iframeLoaded()\"\r\n *ngIf=\"fullUrl\"\r\n #iframe\r\n id=\"iframe\"\r\n frameBorder=\"0\"\r\n [src]=\"fullUrl\"\r\n ></iframe>\r\n </div>\r\n</ng-container>\r\n",
styles: [`
:host {
display: block;
}
.container {
width: 100%;
height: 100%;
position: relative;
}
.overlay-popout-google {
width: 40px;
height: 40px;
right: 26px;
top: 11.5px;
position: absolute;
z-index: 1000;
}
.overlay-popout-office {
width: 100px;
height: 20px;
right: 0;
bottom: 0;
position: absolute;
z-index: 1000;
}
.overlay-full {
width: 100%;
height: 100%;
right: 0;
top: 0;
position: absolute;
z-index: 1000;
}
iframe {
width: 100%;
height: 100%;
}
`]
},] }
];
NgxDocViewerComponent.ctorParameters = () => [
{ type: DomSanitizer },
{ type: NgZone }
];
NgxDocViewerComponent.propDecorators = {
loaded: [{ type: Output }],
url: [{ type: Input }],
queryParams: [{ type: Input }],
viewerUrl: [{ type: Input }],
googleCheckInterval: [{ type: Input }],
googleMaxChecks: [{ type: Input }],
disableContent: [{ type: Input }],
googleCheckContentLoaded: [{ type: Input }],
viewer: [{ type: Input }],
overrideLocalhost: [{ type: Input }],
iframes: [{ type: ViewChildren, args: ['iframe',] }]
};
class NgxDocViewerModule {
}
NgxDocViewerModule.decorators = [
{ type: NgModule, args: [{
imports: [CommonModule],
declarations: [NgxDocViewerComponent],
exports: [NgxDocViewerComponent],
},] }
];
const ɵ0 = () => { };
const defaultProps = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
loaded: ɵ0,
disableContent: 'none',
googleCheckContentLoaded: true,
googleCheckInterval: 3000,
queryParams: '',
url: '',
viewer: 'google',
viewerUrl: '',
};
/**
* Generated bundle index. Do not edit.
*/
export { NgxDocViewerComponent, NgxDocViewerModule, defaultProps, fileToArray, getDocxToHtml, getLocation, getViewerDetails, getbaseUrl, googleCheckSubscription, handleFileUpload, iframeIsLoaded, isLocalFile, replaceLocalUrl, uploadToCloud, ɵ0$1 as ɵ0, ɵ1, ɵ2 };
//# sourceMappingURL=ngx-doc-viewer.js.map