diff --git a/web/document_properties.js b/web/document_properties.js deleted file mode 100644 index 810a627ad..000000000 --- a/web/document_properties.js +++ /dev/null @@ -1,199 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* Copyright 2012 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* globals Promise, mozL10n, getPDFFileNameFromURL, OverlayManager */ - -'use strict'; - -var DocumentProperties = { - overlayName: null, - rawFileSize: 0, - - // Document property fields (in the viewer). - fileNameField: null, - fileSizeField: null, - titleField: null, - authorField: null, - subjectField: null, - keywordsField: null, - creationDateField: null, - modificationDateField: null, - creatorField: null, - producerField: null, - versionField: null, - pageCountField: null, - url: null, - pdfDocument: null, - - initialize: function documentPropertiesInitialize(options) { - this.overlayName = options.overlayName; - - // Set the document property fields. - this.fileNameField = options.fileNameField; - this.fileSizeField = options.fileSizeField; - this.titleField = options.titleField; - this.authorField = options.authorField; - this.subjectField = options.subjectField; - this.keywordsField = options.keywordsField; - this.creationDateField = options.creationDateField; - this.modificationDateField = options.modificationDateField; - this.creatorField = options.creatorField; - this.producerField = options.producerField; - this.versionField = options.versionField; - this.pageCountField = options.pageCountField; - - // Bind the event listener for the Close button. - if (options.closeButton) { - options.closeButton.addEventListener('click', this.close.bind(this)); - } - - this.dataAvailablePromise = new Promise(function (resolve) { - this.resolveDataAvailable = resolve; - }.bind(this)); - - OverlayManager.register(this.overlayName, this.close.bind(this)); - }, - - getProperties: function documentPropertiesGetProperties() { - if (!OverlayManager.active) { - // If the dialog was closed before dataAvailablePromise was resolved, - // don't bother updating the properties. - return; - } - // Get the file size (if it hasn't already been set). - this.pdfDocument.getDownloadInfo().then(function(data) { - if (data.length === this.rawFileSize) { - return; - } - this.setFileSize(data.length); - this.updateUI(this.fileSizeField, this.parseFileSize()); - }.bind(this)); - - // Get the document properties. - this.pdfDocument.getMetadata().then(function(data) { - var fields = [ - { field: this.fileNameField, - content: getPDFFileNameFromURL(this.url) }, - { field: this.fileSizeField, content: this.parseFileSize() }, - { field: this.titleField, content: data.info.Title }, - { field: this.authorField, content: data.info.Author }, - { field: this.subjectField, content: data.info.Subject }, - { field: this.keywordsField, content: data.info.Keywords }, - { field: this.creationDateField, - content: this.parseDate(data.info.CreationDate) }, - { field: this.modificationDateField, - content: this.parseDate(data.info.ModDate) }, - { field: this.creatorField, content: data.info.Creator }, - { field: this.producerField, content: data.info.Producer }, - { field: this.versionField, content: data.info.PDFFormatVersion }, - { field: this.pageCountField, content: this.pdfDocument.numPages } - ]; - - // Show the properties in the dialog. - for (var item in fields) { - var element = fields[item]; - this.updateUI(element.field, element.content); - } - }.bind(this)); - }, - - updateUI: function documentPropertiesUpdateUI(field, content) { - if (field && content !== undefined && content !== '') { - field.textContent = content; - } - }, - - setFileSize: function documentPropertiesSetFileSize(fileSize) { - if (fileSize > 0) { - this.rawFileSize = fileSize; - } - }, - - parseFileSize: function documentPropertiesParseFileSize() { - var fileSize = this.rawFileSize, kb = fileSize / 1024; - if (!kb) { - return; - } else if (kb < 1024) { - return mozL10n.get('document_properties_kb', { - size_kb: (+kb.toPrecision(3)).toLocaleString(), - size_b: fileSize.toLocaleString() - }, '{{size_kb}} KB ({{size_b}} bytes)'); - } else { - return mozL10n.get('document_properties_mb', { - size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(), - size_b: fileSize.toLocaleString() - }, '{{size_mb}} MB ({{size_b}} bytes)'); - } - }, - - open: function documentPropertiesOpen() { - Promise.all([OverlayManager.open(this.overlayName), - this.dataAvailablePromise]).then(function () { - this.getProperties(); - }.bind(this)); - }, - - close: function documentPropertiesClose() { - OverlayManager.close(this.overlayName); - }, - - parseDate: function documentPropertiesParseDate(inputDate) { - // This is implemented according to the PDF specification, but note that - // Adobe Reader doesn't handle changing the date to universal time - // and doesn't use the user's time zone (they're effectively ignoring - // the HH' and mm' parts of the date string). - var dateToParse = inputDate; - if (dateToParse === undefined) { - return ''; - } - - // Remove the D: prefix if it is available. - if (dateToParse.substring(0,2) === 'D:') { - dateToParse = dateToParse.substring(2); - } - - // Get all elements from the PDF date string. - // JavaScript's Date object expects the month to be between - // 0 and 11 instead of 1 and 12, so we're correcting for this. - var year = parseInt(dateToParse.substring(0,4), 10); - var month = parseInt(dateToParse.substring(4,6), 10) - 1; - var day = parseInt(dateToParse.substring(6,8), 10); - var hours = parseInt(dateToParse.substring(8,10), 10); - var minutes = parseInt(dateToParse.substring(10,12), 10); - var seconds = parseInt(dateToParse.substring(12,14), 10); - var utRel = dateToParse.substring(14,15); - var offsetHours = parseInt(dateToParse.substring(15,17), 10); - var offsetMinutes = parseInt(dateToParse.substring(18,20), 10); - - // As per spec, utRel = 'Z' means equal to universal time. - // The other cases ('-' and '+') have to be handled here. - if (utRel === '-') { - hours += offsetHours; - minutes += offsetMinutes; - } else if (utRel === '+') { - hours -= offsetHours; - minutes -= offsetMinutes; - } - - // Return the new date format from the user's locale. - var date = new Date(Date.UTC(year, month, day, hours, minutes, seconds)); - var dateString = date.toLocaleDateString(); - var timeString = date.toLocaleTimeString(); - return mozL10n.get('document_properties_date_string', - {date: dateString, time: timeString}, - '{{date}}, {{time}}'); - } -}; diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js new file mode 100644 index 000000000..51e36ec59 --- /dev/null +++ b/web/pdf_document_properties.js @@ -0,0 +1,226 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* Copyright 2012 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* globals Promise, mozL10n, getPDFFileNameFromURL, OverlayManager */ + +'use strict'; + +/** + * @typedef {Object} PDFDocumentPropertiesOptions + * @property {string} overlayName - Name/identifier for the overlay. + * @property {Object} fields - Names and elements of the overlay's fields. + * @property {HTMLButtonElement} closeButton - Button for closing the overlay. + */ + +/** + * @class + */ +var PDFDocumentProperties = (function PDFDocumentPropertiesClosure() { + /** + * @constructs PDFDocumentProperties + * @param {PDFDocumentPropertiesOptions} options + */ + function PDFDocumentProperties(options) { + this.fields = options.fields; + this.overlayName = options.overlayName; + + this.rawFileSize = 0; + this.url = null; + this.pdfDocument = null; + + // Bind the event listener for the Close button. + if (options.closeButton) { + options.closeButton.addEventListener('click', this.close.bind(this)); + } + + this.dataAvailablePromise = new Promise(function (resolve) { + this.resolveDataAvailable = resolve; + }.bind(this)); + + OverlayManager.register(this.overlayName, this.close.bind(this)); + } + + PDFDocumentProperties.prototype = { + /** + * Open the document properties overlay. + */ + open: function PDFDocumentProperties_open() { + Promise.all([OverlayManager.open(this.overlayName), + this.dataAvailablePromise]).then(function () { + this._getProperties(); + }.bind(this)); + }, + + /** + * Close the document properties overlay. + */ + close: function PDFDocumentProperties_close() { + OverlayManager.close(this.overlayName); + }, + + /** + * Set the file size of the PDF document. This method is used to + * update the file size in the document properties overlay once it + * is known so we do not have to wait until the entire file is loaded. + * + * @param {number} fileSize - The file size of the PDF document. + */ + setFileSize: function PDFDocumentProperties_setFileSize(fileSize) { + if (fileSize > 0) { + this.rawFileSize = fileSize; + } + }, + + /** + * Set a reference to the PDF document and the URL in order + * to populate the overlay fields with the document properties. + * Note that the overlay will contain no information if this method + * is not called. + * + * @param {Object} pdfDocument - A reference to the PDF document. + * @param {string} url - The URL of the document. + */ + setDocumentAndUrl: + function PDFDocumentProperties_setDocumentAndUrl(pdfDocument, url) { + this.pdfDocument = pdfDocument; + this.url = url; + this.resolveDataAvailable(); + }, + + /** + * @private + */ + _getProperties: function PDFDocumentProperties_getProperties() { + if (!OverlayManager.active) { + // If the dialog was closed before dataAvailablePromise was resolved, + // don't bother updating the properties. + return; + } + // Get the file size (if it hasn't already been set). + this.pdfDocument.getDownloadInfo().then(function(data) { + if (data.length === this.rawFileSize) { + return; + } + this.setFileSize(data.length); + this._updateUI(this.fields['fileSize'], this._parseFileSize()); + }.bind(this)); + + // Get the document properties. + this.pdfDocument.getMetadata().then(function(data) { + var content = { + 'fileName': getPDFFileNameFromURL(this.url), + 'fileSize': this._parseFileSize(), + 'title': data.info.Title, + 'author': data.info.Author, + 'subject': data.info.Subject, + 'keywords': data.info.Keywords, + 'creationDate': this._parseDate(data.info.CreationDate), + 'modificationDate': this._parseDate(data.info.ModDate), + 'creator': data.info.Creator, + 'producer': data.info.Producer, + 'version': data.info.PDFFormatVersion, + 'pageCount': this.pdfDocument.numPages + }; + + // Show the properties in the dialog. + for (var identifier in content) { + this._updateUI(this.fields[identifier], content[identifier]); + } + }.bind(this)); + }, + + /** + * @private + */ + _updateUI: function PDFDocumentProperties_updateUI(field, content) { + if (field && content !== undefined && content !== '') { + field.textContent = content; + } + }, + + /** + * @private + */ + _parseFileSize: function PDFDocumentProperties_parseFileSize() { + var fileSize = this.rawFileSize, kb = fileSize / 1024; + if (!kb) { + return; + } else if (kb < 1024) { + return mozL10n.get('document_properties_kb', { + size_kb: (+kb.toPrecision(3)).toLocaleString(), + size_b: fileSize.toLocaleString() + }, '{{size_kb}} KB ({{size_b}} bytes)'); + } else { + return mozL10n.get('document_properties_mb', { + size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(), + size_b: fileSize.toLocaleString() + }, '{{size_mb}} MB ({{size_b}} bytes)'); + } + }, + + /** + * @private + */ + _parseDate: function PDFDocumentProperties_parseDate(inputDate) { + // This is implemented according to the PDF specification, but note that + // Adobe Reader doesn't handle changing the date to universal time + // and doesn't use the user's time zone (they're effectively ignoring + // the HH' and mm' parts of the date string). + var dateToParse = inputDate; + if (dateToParse === undefined) { + return ''; + } + + // Remove the D: prefix if it is available. + if (dateToParse.substring(0,2) === 'D:') { + dateToParse = dateToParse.substring(2); + } + + // Get all elements from the PDF date string. + // JavaScript's Date object expects the month to be between + // 0 and 11 instead of 1 and 12, so we're correcting for this. + var year = parseInt(dateToParse.substring(0,4), 10); + var month = parseInt(dateToParse.substring(4,6), 10) - 1; + var day = parseInt(dateToParse.substring(6,8), 10); + var hours = parseInt(dateToParse.substring(8,10), 10); + var minutes = parseInt(dateToParse.substring(10,12), 10); + var seconds = parseInt(dateToParse.substring(12,14), 10); + var utRel = dateToParse.substring(14,15); + var offsetHours = parseInt(dateToParse.substring(15,17), 10); + var offsetMinutes = parseInt(dateToParse.substring(18,20), 10); + + // As per spec, utRel = 'Z' means equal to universal time. + // The other cases ('-' and '+') have to be handled here. + if (utRel === '-') { + hours += offsetHours; + minutes += offsetMinutes; + } else if (utRel === '+') { + hours -= offsetHours; + minutes -= offsetMinutes; + } + + // Return the new date format from the user's locale. + var date = new Date(Date.UTC(year, month, day, hours, minutes, seconds)); + var dateString = date.toLocaleDateString(); + var timeString = date.toLocaleTimeString(); + return mozL10n.get('document_properties_date_string', + {date: dateString, time: timeString}, + '{{date}}, {{time}}'); + } + }; + + return PDFDocumentProperties; +})(); diff --git a/web/secondary_toolbar.js b/web/secondary_toolbar.js index 0b3f3d596..2714d79f6 100644 --- a/web/secondary_toolbar.js +++ b/web/secondary_toolbar.js @@ -25,7 +25,6 @@ var SecondaryToolbar = { initialize: function secondaryToolbarInitialize(options) { this.toolbar = options.toolbar; - this.documentProperties = options.documentProperties; this.buttonContainer = this.toolbar.firstElementChild; // Define the toolbar buttons. @@ -115,7 +114,7 @@ var SecondaryToolbar = { }, documentPropertiesClick: function secondaryToolbarDocumentPropsClick(evt) { - this.documentProperties.open(); + PDFViewerApplication.pdfDocumentProperties.open(); this.close(); }, diff --git a/web/viewer.html b/web/viewer.html index 70144eb9f..e7dad912b 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -86,7 +86,7 @@ http://sourceforge.net/adobe/cmap/wiki/License/ - + diff --git a/web/viewer.js b/web/viewer.js index 33e11f1d5..2cc02e566 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -19,7 +19,7 @@ PDFHistory, Preferences, SidebarView, ViewHistory, Stats, PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar, PasswordPrompt, PDFPresentationMode, HandTool, Promise, - DocumentProperties, PDFOutlineView, PDFAttachmentView, + PDFDocumentProperties, PDFOutlineView, PDFAttachmentView, OverlayManager, PDFFindController, PDFFindBar, getVisibleElements, watchScroll, PDFViewer, PDFRenderingQueue, PresentationModeState, RenderingStates, DEFAULT_SCALE, UNKNOWN_SCALE, @@ -88,7 +88,7 @@ var mozL10n = document.mozL10n || document.webL10n; //#include hand_tool.js //#include overlay_manager.js //#include password_prompt.js -//#include document_properties.js +//#include pdf_document_properties.js //#include pdf_viewer.js //#include pdf_thumbnail_viewer.js //#include pdf_outline_view.js @@ -109,6 +109,8 @@ var PDFViewerApplication = { pdfRenderingQueue: null, /** @type {PDFPresentationMode} */ pdfPresentationMode: null, + /** @type {PDFDocumentProperties} */ + pdfDocumentProperties: null, pageRotation: 0, updateScaleControls: true, isInitialViewSet: false, @@ -172,6 +174,25 @@ var PDFViewerApplication = { toggleHandTool: document.getElementById('toggleHandTool') }); + this.pdfDocumentProperties = new PDFDocumentProperties({ + overlayName: 'documentPropertiesOverlay', + closeButton: document.getElementById('documentPropertiesClose'), + fields: { + 'fileName': document.getElementById('fileNameField'), + 'fileSize': document.getElementById('fileSizeField'), + 'title': document.getElementById('titleField'), + 'author': document.getElementById('authorField'), + 'subject': document.getElementById('subjectField'), + 'keywords': document.getElementById('keywordsField'), + 'creationDate': document.getElementById('creationDateField'), + 'modificationDate': document.getElementById('modificationDateField'), + 'creator': document.getElementById('creatorField'), + 'producer': document.getElementById('producerField'), + 'version': document.getElementById('versionField'), + 'pageCount': document.getElementById('pageCountField') + } + }); + SecondaryToolbar.initialize({ toolbar: document.getElementById('secondaryToolbar'), toggleButton: document.getElementById('secondaryToolbarToggle'), @@ -185,7 +206,6 @@ var PDFViewerApplication = { lastPage: document.getElementById('lastPage'), pageRotateCw: document.getElementById('pageRotateCw'), pageRotateCcw: document.getElementById('pageRotateCcw'), - documentProperties: DocumentProperties, documentPropertiesButton: document.getElementById('documentProperties') }); @@ -216,23 +236,6 @@ var PDFViewerApplication = { passwordCancel: document.getElementById('passwordCancel') }); - DocumentProperties.initialize({ - overlayName: 'documentPropertiesOverlay', - closeButton: document.getElementById('documentPropertiesClose'), - fileNameField: document.getElementById('fileNameField'), - fileSizeField: document.getElementById('fileSizeField'), - titleField: document.getElementById('titleField'), - authorField: document.getElementById('authorField'), - subjectField: document.getElementById('subjectField'), - keywordsField: document.getElementById('keywordsField'), - creationDateField: document.getElementById('creationDateField'), - modificationDateField: document.getElementById('modificationDateField'), - creatorField: document.getElementById('creatorField'), - producerField: document.getElementById('producerField'), - versionField: document.getElementById('versionField'), - pageCountField: document.getElementById('pageCountField') - }); - var self = this; var initializedPromise = Promise.all([ Preferences.get('enableWebGL').then(function resolved(value) { @@ -410,7 +413,8 @@ var PDFViewerApplication = { pdfDataRangeTransport); if (args.length) { - DocumentProperties.setFileSize(args.length); + PDFViewerApplication.pdfDocumentProperties + .setFileSize(args.length); } break; case 'range': @@ -556,7 +560,7 @@ var PDFViewerApplication = { ); if (args && args.length) { - DocumentProperties.setFileSize(args.length); + PDFViewerApplication.pdfDocumentProperties.setFileSize(args.length); } }, @@ -853,9 +857,7 @@ var PDFViewerApplication = { this.pdfDocument = pdfDocument; - DocumentProperties.url = this.url; - DocumentProperties.pdfDocument = pdfDocument; - DocumentProperties.resolveDataAvailable(); + this.pdfDocumentProperties.setDocumentAndUrl(pdfDocument, this.url); var downloadedPromise = pdfDocument.getDownloadInfo().then(function() { self.downloadComplete = true;