diff options
Diffstat (limited to 'chrome/browser/resources/print_preview')
83 files changed, 9521 insertions, 5462 deletions
diff --git a/chrome/browser/resources/print_preview/cloud_print_interface.js b/chrome/browser/resources/print_preview/cloud_print_interface.js new file mode 100644 index 0000000..b7e7c7c --- /dev/null +++ b/chrome/browser/resources/print_preview/cloud_print_interface.js @@ -0,0 +1,304 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('cloudprint', function() { + 'use strict'; + + /** + * API to the Google Cloud Print service. + * @param {string} baseUrl Base part of the Google Cloud Print service URL + * with no trailing slash. For example, + * 'https://www.google.com/cloudprint'. + * @constructor + * @extends {cr.EventTarget} + */ + function CloudPrintInterface(baseUrl) { + /** + * The base URL of the Google Cloud Print API. + * @type {string} + * @private + */ + this.baseURL_ = baseUrl; + + /** + * Last received XSRF token. Sent as a parameter in every request. + * @type {string} + * @private + */ + this.xsrfToken_ = ''; + }; + + /** + * Event types dispatched by the interface. + * @enum {string} + */ + CloudPrintInterface.EventType = { + ERROR: 'cloudprint.CloudPrintInterface.ERROR', + PRINTER_DONE: 'cloudprint.CloudPrintInterface.PRINTER_DONE', + SEARCH_DONE: 'cloudprint.CloudPrintInterface.SEARCH_DONE', + SUBMIT_DONE: 'cloudprint.CloudPrintInterface.SUBMIT_DONE' + }; + + /** + * Content type header value for a URL encoded HTTP request. + * @type {string} + * @private + */ + CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_ = + 'application/x-www-form-urlencoded'; + + /** + * Content type header value for a multipart HTTP request. + * @type {string} + * @private + */ + CloudPrintInterface.MULTIPART_CONTENT_TYPE_ = + 'multipart/form-data; boundary=----CloudPrintFormBoundaryjc9wuprokl8i'; + + /** + * Enumeration of JSON response fields from Google Cloud Print API. + * @enum {string} + * @private + */ + CloudPrintInterface.JsonFields_ = { + PRINTER: 'printer' + }; + + CloudPrintInterface.prototype = { + __proto__: cr.EventTarget.prototype, + + /** + * Sends a Google Cloud Print search API request. + * @param {boolean} isRecent Whether to search for only recently used + * printers. + */ + search: function(isRecent) { + var params = {}; + if (isRecent) { + params['q'] = '^recent'; + } + this.sendRequest_('GET', 'search', params, null, this.onSearchDone_); + }, + + /** + * Sends a Google Cloud Print submit API request. + * @param {string} body Body of the HTTP post request to send. + */ + submit: function(body) { + this.sendRequest_('POST', 'submit', null, body, this.onSubmitDone_); + }, + + /** + * Sends a Google Cloud Print printer API request. + * @param {string} printerId ID of the printer to lookup. + */ + printer: function(printerId) { + var params = {'printerid': printerId}; + this.sendRequest_('GET', 'printer', params, null, this.onPrinterDone_); + }, + + /** + * Creates an object that represents a Google Cloud Print print ticket. + * @param {!print_preview.Destination} destination Destination to print to. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to create + * the state of the print ticket. + * @return {object} Google Cloud Print print ticket. + */ + createPrintTicket: function(destination, printTicketStore) { + assert(!destination.isLocal, + 'Trying to create a Google Cloud Print print ticket for a local ' + + 'destination'); + assert(destination.capabilities, + 'Trying to create a Google Cloud Print print ticket for a ' + + 'destination with no print capabilities'); + + var ticketItems = []; + + if (destination.capabilities.collateCapability) { + var collateCap = destination.capabilities.collateCapability; + var ticketItem = { + 'name': collateCap.id, + 'type': collateCap.type, + 'options': [{'name': printTicketStore.isCollateEnabled() ? + collateCap.collateOption : collateCap.noCollateOption}] + }; + ticketItems.push(ticketItem); + } + + if (destination.capabilities.colorCapability) { + var colorCap = destination.capabilities.colorCapability; + var ticketItem = { + 'name': colorCap.id, + 'type': colorCap.type, + 'options': [{'name': printTicketStore.isColorEnabled() ? + colorCap.colorOption : colorCap.bwOption}] + }; + ticketItems.push(ticketItem); + } + + if (destination.capabilities.copiesCapability) { + var copiesCap = destination.capabilities.copiesCapability; + var ticketItem = { + 'name': copiesCap.id, + 'type': copiesCap.type, + 'value': printTicketStore.getCopies() + }; + ticketItems.push(ticketItem); + } + + if (destination.capabilities.duplexCapability) { + var duplexCap = destination.capabilities.duplexCapability; + var ticketItem = { + 'name': duplexCap.id, + 'type': duplexCap.type, + 'options': [{'name': printTicketStore.isDuplexEnabled() ? + duplexCap.longEdgeOption : duplexCap.simplexOption}] + }; + ticketItems.push(ticketItem); + } + + return { + 'capabilities': ticketItems + }; + }, + + /** + * Sends a request to the Google Cloud Print API. + * @param {string} method HTTP method of the request. + * @param {string} action Google Cloud Print action to perform. + * @param {Object} params HTTP parameters to include in the request. + * @param {string} body HTTP multi-part encoded body. + * @param {function(Object)} successCallback Callback to invoke when request + * completes successfully. + */ + sendRequest_: function(method, action, params, body, successCallback) { + if (!this.xsrfToken_) { + // TODO(rltoscano): Should throw an error if not a read-only action or + // issue an xsrf token request. + } + var url = this.baseURL_ + '/' + action + '?xsrf=' + this.xsrfToken_; + + if (params) { + for (var paramName in params) { + url += '&' + paramName + '=' + encodeURIComponent(params[paramName]); + } + } + + var headers = {}; + headers['X-CloudPrint-Proxy'] = 'ChromePrintPreview'; + if (method == 'GET') { + headers['Content-Type'] = CloudPrintInterface.URL_ENCODED_CONTENT_TYPE_; + } else if (method == 'POST') { + headers['Content-Type'] = CloudPrintInterface.MULTIPART_CONTENT_TYPE_; + } + + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = this.onReadyStateChange_.bind( + this, xhr, successCallback.bind(this)); + xhr.open(method, url, true); + xhr.withCredentials = true; + for (var header in headers) { + xhr.setRequestHeader(header, headers[header]); + } + xhr.send(body); + }, + + /** + * Dispatches an ERROR event with the given error message. + * @param {string} message Error message to include in the ERROR event. + * @private + */ + dispatchErrorEvent_: function(message) { + var errorEvent = new cr.Event(CloudPrintInterface.EventType.ERROR); + errorEvent.message = message; + this.dispatchEvent(errorEvent); + }, + + /** + * Called when the ready-state of a XML http request changes. + * Calls the successCallback with the result or dispatches an ERROR event. + * @param {XMLHttpRequest} xhr XML http request that changed. + * @param {function(Object)} successCallback Callback to call if the request + * was successful. + * @private + */ + onReadyStateChange_: function(xhr, successCallback) { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var result = JSON.parse(xhr.responseText); + if (result['success']) { + this.xsrfToken_ = result['xsrf_token']; + successCallback(result); + } else { + this.dispatchErrorEvent_(result['message']); + } + } else { + this.dispatchErrorEvent_(xhr.status + ''); + } + } + }, + + /** + * Called when the search request completes successfully. + * @param {Object} result JSON response. + * @private + */ + onSearchDone_: function(result) { + var printerListJson = result['printers'] || []; + var printerList = []; + for (var printerJson, i = 0; printerJson = printerListJson[i]; i++) { + try { + printerList.push( + cloudprint.CloudDestinationParser.parse(printerJson)); + } catch (err) { + console.error('Unable to parse cloud print destination: ' + err); + } + } + var isRecent = result['request']['params']['q'] == '^recent'; + var searchDoneEvent = + new cr.Event(CloudPrintInterface.EventType.SEARCH_DONE); + searchDoneEvent.printers = printerList; + searchDoneEvent.isRecent = isRecent; + searchDoneEvent.email = result['request']['user']; + this.dispatchEvent(searchDoneEvent); + }, + + /** + * Called when the submit request completes successfully. + * @param {Object} result JSON response. + * @private + */ + onSubmitDone_: function(result) { + this.dispatchEvent( + new cr.Event(CloudPrintInterface.EventType.SUBMIT_DONE)); + }, + + /** + * Called when the printer request completes successfully. + * @param {Object} result JSON response. + * @private + */ + onPrinterDone_: function(result) { + // TODO(rltoscano): Better error handling here. + var printerJson = result['printers'][0]; + var printer; + try { + printer = cloudprint.CloudDestinationParser.parse(printerJson); + } catch (err) { + console.error('Failed to parse cloud print destination: ' + + JSON.stringify(printerJson)); + return; + } + var printerDoneEvent = + new cr.Event(CloudPrintInterface.EventType.PRINTER_DONE); + printerDoneEvent.printer = printer; + this.dispatchEvent(printerDoneEvent); + } + }; + + // Export + return { + CloudPrintInterface: CloudPrintInterface + }; +}); diff --git a/chrome/browser/resources/print_preview/color_settings.html b/chrome/browser/resources/print_preview/color_settings.html deleted file mode 100644 index 7eadd0a..0000000 --- a/chrome/browser/resources/print_preview/color_settings.html +++ /dev/null @@ -1,14 +0,0 @@ -<div id="color-option" class="two-column visible" aria-hidden="true" - aria-live="polite"> - <h1 i18n-content="optionColor"></h1> - <div class="right-column"> - <div class="radio"><label> - <input id="color" type="radio" name="color"> - <span i18n-content="optionColor"></span> - </label></div> - <div class="radio"><label> - <input id="bw" type="radio" name="color" checked> - <span i18n-content="optionBw"></span> - </label></div> - </div> - </div> diff --git a/chrome/browser/resources/print_preview/color_settings.js b/chrome/browser/resources/print_preview/color_settings.js deleted file mode 100644 index 2d9cdc2..0000000 --- a/chrome/browser/resources/print_preview/color_settings.js +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a ColorSettings object. This object encapsulates all settings and - * logic related to color selection (color/bw). - * @constructor - */ - function ColorSettings() { - this.colorOption_ = $('color-option'); - this.colorRadioButton_ = $('color'); - this.bwRadioButton_ = $('bw'); - - this.printerColorModelForColor_ = ColorSettings.COLOR; - this.printerColorModelForBlack_ = ColorSettings.GRAY; - this.addEventListeners_(); - } - - ColorSettings.GRAY = 1; - ColorSettings.COLOR = 2; - cr.addSingletonGetter(ColorSettings); - - ColorSettings.prototype = { - /** - * The radio button corresponding to the color option. - * @type {HTMLInputElement} - */ - get colorRadioButton() { - return this.colorRadioButton_; - }, - - /** - * The radio button corresponding to the black and white option. - * @type {HTMLInputElement} - */ - get bwRadioButton() { - return this.bwRadioButton_; - }, - - /** - * @return {number} The color mode for print preview. - */ - get colorMode() { - return this.bwRadioButton_.checked ? - this.printerColorModelForBlack_ : - this.printerColorModelForColor_; - }, - - /** - * Adding listeners to all color related controls. The listeners take care - * of altering their behavior depending on |hasPendingPreviewRequest|. - * @private - */ - addEventListeners_: function() { - this.colorRadioButton_.onclick = function() { - setColor(true); - }; - this.bwRadioButton_.onclick = function() { - setColor(false); - }; - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - document.addEventListener(customEvents.PRINTER_CAPABILITIES_UPDATED, - this.onPrinterCapabilitiesUpdated_.bind(this)); - }, - - /** - * Executes when a |customEvents.PRINTER_CAPABILITIES_UPDATED| event occurs. - * @private - */ - onPrinterCapabilitiesUpdated_: function(e) { - var disableColorOption = e.printerCapabilities.disableColorOption; - - disableColorOption ? fadeOutOption(this.colorOption_) : - fadeInOption(this.colorOption_); - this.colorOption_.setAttribute('aria-hidden', disableColorOption); - - var setColorAsDefault = e.printerCapabilities.setColorAsDefault; - this.printerColorModelForColor_ = - e.printerCapabilities.printerColorModelForColor || - ColorSettings.COLOR; - this.printerColorModelForBlack_ = - e.printerCapabilities.printerColorModelForBlack || - ColorSettings.GRAY; - - this.colorRadioButton_.checked = setColorAsDefault; - this.bwRadioButton_.checked = !setColorAsDefault; - setColor(this.colorRadioButton_.checked); - }, - - /** - * Executes when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - setColor(this.colorRadioButton_.checked); - } - - }; - - return { - ColorSettings: ColorSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/component.js b/chrome/browser/resources/print_preview/component.js new file mode 100644 index 0000000..f51c3ae1 --- /dev/null +++ b/chrome/browser/resources/print_preview/component.js @@ -0,0 +1,192 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Class that represents a UI component. + * @constructor + * @extends {cr.EventTarget} + */ + function Component() { + cr.EventTarget.call(this); + + /** + * Component's HTML element. + * @type {Element} + * @private + */ + this.element_ = null; + + this.isInDocument_ = false; + + /** + * Component's event tracker. + * @type {EventTracker} + * @private + */ + this.tracker_ = new EventTracker(); + + /** + * Child components of the component. + * @type {Array.<print_preview.Component>} + * @private + */ + this.children_ = []; + }; + + Component.prototype = { + __proto__: cr.EventTarget.prototype, + + /** Gets the component's element. */ + getElement: function() { + return this.element_; + }, + + /** @return {EventTracker} Component's event tracker. */ + get tracker() { + return this.tracker_; + }, + + /** + * @return {boolean} Whether the element of the component is already in the + * HTML document. + */ + get isInDocument() { + return this.isInDocument_; + }, + + /** + * Creates the root element of the component. Sub-classes should override + * this method. + */ + createDom: function() { + this.element_ = cr.doc.createElement('div'); + }, + + /** + * Called when the component's element is known to be in the document. + * Anything using document.getElementById etc. should be done at this stage. + * Sub-classes should extend this method and attach listeners. + */ + enterDocument: function() { + this.isInDocument_ = true; + for (var child, i = 0; child = this.children_[i]; i++) { + if (!child.isInDocument && child.getElement()) { + child.enterDocument(); + } + } + }, + + /** Removes all event listeners. */ + exitDocument: function() { + for (var child, i = 0; child = this.children_[i]; i++) { + if (child.isInDocument) { + child.exitDocument(); + } + } + this.tracker_.removeAll(); + this.isInDocument_ = false; + }, + + /** + * Renders this UI component and appends the element to the given parent + * element. + * @param {!Element} parentElement Element to render the component's + * element into. + */ + render: function(parentElement) { + assert(!this.isInDocument, 'Component is already in the document'); + if (!this.element_) { + this.createDom(); + } + parentElement.appendChild(this.element_); + this.enterDocument(); + }, + + /** + * Decorates an existing DOM element. Sub-classes should override the + * override the decorateInternal method. + * @param {Element} element Element to decorate. + */ + decorate: function(element) { + assert(!this.isInDocument, 'Component is already in the document'); + this.setElementInternal(element); + this.decorateInternal(); + this.enterDocument(); + }, + + /** + * @param {print_preview.Component} child Component to add as a child of + * this component. + */ + addChild: function(child) { + this.children_.push(child); + }, + + /** + * @param {!print_preview.Component} child Component to remove from this + * component's children. + */ + removeChild: function(child) { + var childIdx = this.children_.indexOf(child); + if (childIdx != -1) { + this.children_.splice(childIdx, 1); + } + if (child.isInDocument) { + child.exitDocument(); + if (child.getElement()) { + child.getElement().parentNode.removeChild(child.getElement()); + } + } + }, + + /** Removes all of the component's children. */ + removeChildren: function() { + while (this.children_.length > 0) { + this.removeChild(this.children_[0]); + } + }, + + /** + * Sets the component's element. + * @param {Element} element HTML element to set as the component's element. + * @protected + */ + setElementInternal: function(element) { + this.element_ = element; + }, + + /** + * Decorates the given element for use as the element of the component. + * @protected + */ + decorateInternal: function() { /*abstract*/ }, + + /** + * Clones a template HTML DOM tree. + * @param {string} templateId Template element ID. + * @param {boolean=} opt_keepHidden Whether to leave the cloned template + * hidden after cloning. + * @return {Element} Cloned element with its 'id' attribute stripped. + * @protected + */ + cloneTemplateInternal: function(templateId, opt_keepHidden) { + var templateEl = $(templateId); + assert(templateEl != null, + 'Could not find element with ID: ' + templateId); + var el = templateEl.cloneNode(true); + el.id = ''; + if (!opt_keepHidden) { + setIsVisible(el, true); + } + return el; + } + }; + + return { + Component: Component + }; +}); diff --git a/chrome/browser/resources/print_preview/copies_settings.html b/chrome/browser/resources/print_preview/copies_settings.html deleted file mode 100644 index f368a6b..0000000 --- a/chrome/browser/resources/print_preview/copies_settings.html +++ /dev/null @@ -1,27 +0,0 @@ -<div id="copies-option" class="two-column visible"> - <h1 i18n-content="copiesLabel"></h1> - <div class="right-column"> - <div> - <input id="copies" type="text" value="1" maxlength="3"> - <button id="increment" - i18n-values="title:incrementTitle;">+</button> - <button id="decrement" - i18n-values="title:decrementTitle;">–</button> - <div id="collate-option" class="checkbox" aria-live="polite" hidden> - <label> - <input id="collate" type="checkbox" checked> - <span i18n-content="optionCollate"></span> - </label> - </div> - </div> - <span id="copies-hint" class="hint" - i18n-content="copiesInstruction" aria-live="polite"> - </span> - <div class="checkbox"> - <label id="two-sided-option" aria-live="polite"> - <input id="two-sided" type="checkbox"> - <span i18n-content="optionTwoSided"></span> - </label> - </div> - </div> -</div> diff --git a/chrome/browser/resources/print_preview/copies_settings.js b/chrome/browser/resources/print_preview/copies_settings.js deleted file mode 100644 index b5b94e9..0000000 --- a/chrome/browser/resources/print_preview/copies_settings.js +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a CopiesSettings object. - * @constructor - */ - function CopiesSettings() { - this.copiesOption_ = $('copies-option'); - this.textfield_ = $('copies'); - this.incrementButton_ = $('increment'); - this.decrementButton_ = $('decrement'); - // Minimum allowed value for number of copies. - this.minValue_ = 1; - // Maximum allowed value for number of copies. - this.maxValue_ = 999; - this.collateOption_ = $('collate-option'); - this.collateCheckbox_ = $('collate'); - this.hint_ = $('copies-hint'); - this.twoSidedCheckbox_ = $('two-sided'); - this.twoSidedOption_ = $('two-sided-option'); - - this.previousDuplexMode_ = CopiesSettings.UNKNOWN_DUPLEX_MODE; - this.addEventListeners_(); - } - - // Constant values matches printing::DuplexMode enum. - CopiesSettings.SIMPLEX = 0; - CopiesSettings.LONG_EDGE = 1; - CopiesSettings.UNKNOWN_DUPLEX_MODE = -1; - - cr.addSingletonGetter(CopiesSettings); - - CopiesSettings.prototype = { - /** - * The number of copies represented by the contents of |this.textfield_|. - * If the text is not valid returns |this.minValue_|. - * @type {number} - */ - get numberOfCopies() { - var value = parseInt(this.textfield_.value, 10); - if (!value || value <= this.minValue_) - value = this.minValue_; - return value; - }, - - /** - * Getter method for |collateOption_|. - * @type {HTMLElement} - */ - get collateOption() { - return this.collateOption_; - }, - - /** - * Getter method for |twoSidedCheckbox_|. - * @type {HTMLInputElement} - */ - get twoSidedCheckbox() { - return this.twoSidedCheckbox_; - }, - - /** - * Gets the duplex mode information for printing. - * @return {number} duplex mode. - */ - get duplexMode() { - if (this.twoSidedOption_.hidden) - return CopiesSettings.UNKNOWN_DUPLEX_MODE; - else if (this.twoSidedCheckbox_.checked) - return CopiesSettings.LONG_EDGE; - else - return CopiesSettings.SIMPLEX; - }, - - set previousDuplexMode(duplexMode) { - this.previousDuplexMode_ = duplexMode; - }, - - /** - * @return {boolean} true if |this.textfield_| is empty, or represents a - * positive integer value. - */ - isValid: function() { - return !this.textfield_.value || isPositiveInteger(this.textfield_.value); - }, - - /** - * Checks whether the preview collate setting value is set or not. - * @return {boolean} true if collate option is enabled and checked. - */ - isCollated: function() { - return !this.collateOption_.hidden && this.collateCheckbox_.checked; - }, - - /** - * Resets |this.textfield_| to |this.minValue_|. - * @private - */ - reset_: function() { - this.textfield_.value = this.minValue_; - }, - - /** - * Listener function to execute whenever the increment/decrement buttons are - * clicked. - * @private - * @param {int} sign Must be 1 for an increment button click and -1 for a - * decrement button click. - */ - onButtonClicked_: function(sign) { - if (!this.isValid()) { - this.reset_(); - } else { - var newValue = this.numberOfCopies + sign; - this.textfield_.value = newValue; - } - this.updateButtonsState_(); - this.showHideCollateOption_(); - - if (!hasPendingPreviewRequest) { - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - } - }, - - /** - * Listener function to execute whenever a change occurs in |textfield_| - * textfield. - * @private - */ - onTextfieldChanged_: function() { - this.updateButtonsState_(); - this.showHideCollateOption_(); - if (!hasPendingPreviewRequest) { - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - } - }, - - /** - * Adding listeners to all copies related controls. The listeners take care - * of altering their behavior depending on |hasPendingPreviewRequest|. - * @private - */ - addEventListeners_: function() { - this.textfield_.oninput = this.onTextfieldChanged_.bind(this); - this.incrementButton_.onclick = this.onIncrementButtonClicked_.bind(this); - this.decrementButton_.onclick = this.onDecrementButtonClicked_.bind(this); - this.twoSidedCheckbox_.onclick = function() { - if (!hasPendingPreviewRequest) - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - } - document.addEventListener(customEvents.PDF_LOADED, - this.updateButtonsState_.bind(this)); - document.addEventListener(customEvents.PRINTER_CAPABILITIES_UPDATED, - this.onPrinterCapabilitiesUpdated_.bind(this)); - }, - - /** - * Executes when a |customEvents.PRINTER_CAPABILITIES_UPDATED| event occurs. - * @private - */ - onPrinterCapabilitiesUpdated_: function(e) { - var duplexValue = e.printerCapabilities.printerDefaultDuplexValue; - if (duplexValue != CopiesSettings.UNKNOWN_DUPLEX_MODE && - this.previousDuplexMode_ != CopiesSettings.UNKNOWN_DUPLEX_MODE) { - duplexValue = this.previousDuplexMode_; - } - this.updateTwoSidedOption_(duplexValue); - e.printerCapabilities.disableCopiesOption ? - fadeOutOption(this.copiesOption_) : - fadeInOption(this.copiesOption_); - }, - - /** - * Listener triggered when |incrementButton_| is clicked. - * @private - */ - onIncrementButtonClicked_: function() { - this.onButtonClicked_(1); - }, - - /** - * Listener triggered when |decrementButton_| is clicked. - * @private - */ - onDecrementButtonClicked_: function() { - this.onButtonClicked_(-1); - }, - - /** - * Takes care of showing/hiding the collate option. - * @private - */ - showHideCollateOption_: function() { - this.collateOption_.hidden = this.numberOfCopies <= 1; - }, - - /* - * Takes care of showing/hiding the two sided option. - * @param {number} defaultDuplexValue Specifies the default duplex value. - * @private - */ - updateTwoSidedOption_: function(defaultDuplexValue) { - // On Windows, some printers don't specify their duplex values in the - // printer schema. If the printer duplex value is UNKNOWN_DUPLEX_MODE, - // hide the two sided option in preview tab UI. - // Ref bug: http://crbug.com/89204 - this.twoSidedOption_.hidden = - (defaultDuplexValue == CopiesSettings.UNKNOWN_DUPLEX_MODE); - - if (!this.twoSidedOption_.hidden) { - this.twoSidedCheckbox_.checked = !!defaultDuplexValue; - if (pageSettings.totalPageCount) - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - } - }, - - /** - * Updates the state of the increment/decrement buttons based on the current - * |textfield_| value. - * @private - */ - updateButtonsState_: function() { - if (!this.isValid()) { - this.textfield_.classList.add('invalid'); - this.incrementButton_.disabled = false; - this.decrementButton_.disabled = false; - fadeInElement(this.hint_); - } else { - this.textfield_.classList.remove('invalid'); - this.incrementButton_.disabled = this.numberOfCopies == this.maxValue_; - this.decrementButton_.disabled = this.numberOfCopies == this.minValue_; - fadeOutElement(this.hint_); - } - this.hint_.setAttribute('aria-hidden', this.isValid()); - } - }; - - return { - CopiesSettings: CopiesSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/data/capabilities_holder.js b/chrome/browser/resources/print_preview/data/capabilities_holder.js new file mode 100644 index 0000000..bb4a1aa --- /dev/null +++ b/chrome/browser/resources/print_preview/data/capabilities_holder.js @@ -0,0 +1,43 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Mutable reference to a capabilities object. + * @constructor + */ + function CapabilitiesHolder() { + /** + * Reference to the capabilities object. + * @type {print_preview.ChromiumCapabilities} + * @private + */ + this.capabilities_ = null; + }; + + CapabilitiesHolder.prototype = { + /** + * @return {print_preview.ChromiumCapabilities} The instance held by the + * holder. + */ + get: function() { + return this.capabilities_; + }, + + /** + * @param {!print_preview.ChromiumCapabilities} New instance to put into the + * holder. + */ + set: function(capabilities) { + this.capabilities_ = capabilities; + } + }; + + // Export + return { + CapabilitiesHolder: CapabilitiesHolder + }; +}); diff --git a/chrome/browser/resources/print_preview/data/chromium_capabilities.js b/chrome/browser/resources/print_preview/data/chromium_capabilities.js new file mode 100644 index 0000000..1cb238d --- /dev/null +++ b/chrome/browser/resources/print_preview/data/chromium_capabilities.js @@ -0,0 +1,180 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Capabilities of a print destination not including the capabilities of the + * document renderer. + * @param {boolean} hasCopiesCapability Whether the print destination has a + * copies capability. + * @param {string} defaultCopiesStr Default string representation of the + * copies value. + * @param {boolean} hasCollateCapability Whether the print destination has + * collation capability. + * @param {boolean} defaultIsCollateEnabled Whether collate is enabled by + * default. + * @param {boolean} hasDuplexCapability Whether the print destination has + * duplexing capability. + * @param {boolean} defaultIsDuplexEnabled Whether duplexing is enabled by + * default. + * @param {boolean} hasOrientationCapability Whether the print destination has + * orientation capability. + * @param {boolean} defaultIsLandscapeEnabled Whether the document should be + * printed in landscape by default. + * @param {boolean} hasColorCapability Whether the print destination has + * color printing capability. + * @param {boolean} defaultIsColorEnabled Whether the document should be + * printed in color by default. + * @constructor + */ + function ChromiumCapabilities( + hasCopiesCapability, + defaultCopiesStr, + hasCollateCapability, + defaultIsCollateEnabled, + hasDuplexCapability, + defaultIsDuplexEnabled, + hasOrientationCapability, + defaultIsLandscapeEnabled, + hasColorCapability, + defaultIsColorEnabled) { + /** + * Whether the print destination has a copies capability. + * @type {boolean} + * @private + */ + this.hasCopiesCapability_ = hasCopiesCapability; + + /** + * Default string representation of the copies value. + * @type {string} + * @private + */ + this.defaultCopiesStr_ = defaultCopiesStr; + + /** + * Whether the print destination has collation capability. + * @type {boolean} + * @private + */ + this.hasCollateCapability_ = hasCollateCapability; + + /** + * Whether collate is enabled by default. + * @type {boolean} + * @private + */ + this.defaultIsCollateEnabled_ = defaultIsCollateEnabled; + + /** + * Whether the print destination has duplexing capability. + * @type {boolean} + * @private + */ + this.hasDuplexCapability_ = hasDuplexCapability; + + /** + * Whether duplex is enabled by default. + * @type {boolean} + * @private + */ + this.defaultIsDuplexEnabled_ = defaultIsDuplexEnabled; + + /** + * Whether the print destination has orientation capability. + * @type {boolean} + * @private + */ + this.hasOrientationCapability_ = hasOrientationCapability; + + /** + * Whether the document should be printed in landscape by default. + * @type {boolean} + * @private + */ + this.defaultIsLandscapeEnabled_ = defaultIsLandscapeEnabled; + + /** + * Whether the print destination has color printing capability. + * @type {boolean} + * @private + */ + this.hasColorCapability_ = hasColorCapability; + + /** + * Whether the document should be printed in color. + * @type {boolean} + * @private + */ + this.defaultIsColorEnabled_ = defaultIsColorEnabled; + }; + + ChromiumCapabilities.prototype = { + /** @return {boolean} Whether the destination has the copies capability. */ + get hasCopiesCapability() { + return this.hasCopiesCapability_; + }, + + /** @return {string} Default number of copies in string format. */ + get defaultCopiesStr() { + return this.defaultCopiesStr_; + }, + + /** @return {boolean} Whether the destination has collation capability. */ + get hasCollateCapability() { + return this.hasCollateCapability_; + }, + + /** @return {boolean} Whether collation is enabled by default. */ + get defaultIsCollateEnabled() { + return this.defaultIsCollateEnabled_; + }, + + /** @return {boolean} Whether the destination has the duplex capability. */ + get hasDuplexCapability() { + return this.hasDuplexCapability_; + }, + + /** @return {boolean} Whether duplexing is enabled by default. */ + get defaultIsDuplexEnabled() { + return this.defaultIsDuplexEnabled_; + }, + + /** + * @return {boolean} Whether the destination has the orientation capability. + */ + get hasOrientationCapability() { + return this.hasOrientationCapability_; + }, + + /** + * @return {boolean} Whether document should be printed in landscape by + * default. + */ + get defaultIsLandscapeEnabled() { + return this.defaultIsLandscapeEnabled_; + }, + + /** + * @return {boolean} Whether the destination has color printing capability. + */ + get hasColorCapability() { + return this.hasColorCapability_; + }, + + /** + * @return {boolean} Whether document should be printed in color by default. + */ + get defaultIsColorEnabled() { + return this.defaultIsColorEnabled_; + } + }; + + // Export + return { + ChromiumCapabilities: ChromiumCapabilities + }; +}); diff --git a/chrome/browser/resources/print_preview/data/cloud_capabilities.js b/chrome/browser/resources/print_preview/data/cloud_capabilities.js new file mode 100644 index 0000000..7bb107f --- /dev/null +++ b/chrome/browser/resources/print_preview/data/cloud_capabilities.js @@ -0,0 +1,413 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Capabilities of a cloud-based print destination. + * @param {print_preview.CollateCapability} collateCapability Print + * destination collate capability. + * @param {print_preview.ColorCapability} colorCapability Print destination + * color capability. + * @param {print_preview.CopiesCapability} copiesCapability Print destination + * copies capability. + * @param {print_preview.DuplexCapability} duplexCapability Print destination + * duplexing capability. + * @constructor + * @extends {print_preview.ChromiumCapabilities} + */ + function CloudCapabilities( + collateCapability, colorCapability, copiesCapability, duplexCapability) { + print_preview.ChromiumCapabilities.call( + this, + !!copiesCapability, + '1' /*defaultCopiesStr*/, + !!collateCapability, + !!collateCapability && collateCapability.isCollateDefault, + !!duplexCapability, + !!duplexCapability && duplexCapability.isDuplexDefault, + true /*hasOrientationCapability*/, + false /*defaultIsLandscapeEnabled*/, + !!colorCapability, + !!colorCapability && colorCapability.isColorDefault); + + /** + * Print destination collate capability. + * @type {print_preview.CollateCapability} + * @private + */ + this.collateCapability_ = collateCapability; + + /** + * Print destination color capability. + * @type {print_preview.ColorCapability} + * @private + */ + this.colorCapability_ = colorCapability; + + /** + * Print destination copies capability. + * @type {print_preview.CopiesCapability} + * @private + */ + this.copiesCapability_ = copiesCapability; + + /** + * Print destination duplexing capability. + * @type {print_preview.DuplexCapability} + * @private + */ + this.duplexCapability_ = duplexCapability; + }; + + /** + * Enumeration of the capability formats of cloud-based print destinations. + * @enum {string} + */ + CloudCapabilities.Format = { + HP: 'hp', + PPD: 'ppd', + XPS: 'xps' + }; + + CloudCapabilities.prototype = { + __proto__: print_preview.ChromiumCapabilities.prototype, + + /** + * @return {print_preview.CollateCapability} The print destination's collate + * capability. + */ + get collateCapability() { + return this.collateCapability_; + }, + + /** + * @return {print_preview.CollateCapability} The print destination's color + * capability. + */ + get colorCapability() { + return this.colorCapability_; + }, + + /** + * @return {print_preview.CollateCapability} The print destination's copies + * capability. + */ + get copiesCapability() { + return this.copiesCapability_; + }, + + /** + * @return {print_preview.CollateCapability} The print destination's + * duplexing capability. + */ + get duplexCapability() { + return this.duplexCapability_; + } + }; + + /** + * A single print capability of a cloud-based print destination. + * @param {string} id Identifier of the capability. + * @param {print_preview.CloudCapability.Type} type Type of the capability. + * @constructor + */ + function CloudCapability(id, type) { + /** + * Identifier of the capability. + * @type {string} + * @private + */ + this.id_ = id; + + /** + * Type of the capability. + * @type {print_preview.CloudCapability.Type} + * @private + */ + this.type_ = type; + }; + + /** + * Enumeration of the types of cloud-based print capabilities. + * @enum {string} + */ + CloudCapability.Type = { + FEATURE: 'Feature', + PARAMETER_DEF: 'ParameterDef' + }; + + CloudCapability.prototype = { + /** @return {string} Identifier of the capability. */ + get id() { + return this.id_; + }, + + /** @return {print_preview.CloudCapability.Type} Type of the capability. */ + get type() { + return this.type_; + } + }; + + /** + * Cloud-based collate capability. + * @param {string} id Identifier of the collate capability. + * @param {string} collateOption Identifier of the option that enables + * collation. + * @param {string} noCollateOption Identifier of the option that disables + * collation. + * @param {boolean} isCollateDefault Whether collation is enabled by default. + * @constructor + * @extends {print_preview.CloudCapability} + */ + function CollateCapability( + id, collateOption, noCollateOption, isCollateDefault) { + CloudCapability.call(this, id, CloudCapability.Type.FEATURE); + + /** + * Identifier of the option that enables collation. + * @type {string} + * @private + */ + this.collateOption_ = collateOption; + + /** + * Identifier of the option that disables collation. + * @type {string} + * @private + */ + this.noCollateOption_ = noCollateOption; + + /** + * Whether collation is enabled by default. + * @type {boolean} + * @private + */ + this.isCollateDefault_ = isCollateDefault; + }; + + /** + * Mapping of capability formats to an identifier of the collate capability. + * @type {object<CloudCapabilities.Format, string>} + */ + CollateCapability.Id = {}; + CollateCapability.Id[CloudCapabilities.Format.PPD] = 'Collate'; + CollateCapability.Id[CloudCapabilities.Format.XPS] = 'psk:DocumentCollate'; + + /** + * Regular expression that matches a collate option. + * @type {!RegExp} + * @const + */ + CollateCapability.COLLATE_REGEX = /(.*:collated.*|true)/i; + + /** + * Regular expression that matches a no-collate option. + * @type {!RegExp} + * @const + */ + CollateCapability.NO_COLLATE_REGEX = /(.*:uncollated.*|false)/i; + + CollateCapability.prototype = { + __proto__: CloudCapability.prototype, + + /** @return {string} Identifier of the option that enables collation. */ + get collateOption() { + return this.collateOption_; + }, + + /** @return {string} Identifier of the option that disables collation. */ + get noCollateOption() { + return this.noCollateOption_; + }, + + /** @return {boolean} Whether collation is enabled by default. */ + get isCollateDefault() { + return this.isCollateDefault_; + } + }; + + /** + * Cloud-based color print capability. + * @param {string} id Identifier of the color capability. + * @param {string} colorOption Identifier of the color option. + * @param {string} bwOption Identifier of the black-white option. + * @param {boolean} Whether color printing is enabled by default. + * @constructor + */ + function ColorCapability(id, colorOption, bwOption, isColorDefault) { + CloudCapability.call(this, id, CloudCapability.Type.FEATURE); + + /** + * Identifier of the color option. + * @type {string} + * @private + */ + this.colorOption_ = colorOption; + + /** + * Identifier of the black-white option. + * @type {string} + * @private + */ + this.bwOption_ = bwOption; + + /** + * Whether to print in color by default. + * @type {boolean} + * @private + */ + this.isColorDefault_ = isColorDefault; + }; + + /** + * Mapping of capability formats to an identifier of the color capability. + * @type {object<CloudCapabilities.Format, string>} + */ + ColorCapability.Id = {}; + ColorCapability.Id[CloudCapabilities.Format.HP] = 'ns1:Colors'; + ColorCapability.Id[CloudCapabilities.Format.PPD] = 'ColorModel'; + ColorCapability.Id[CloudCapabilities.Format.XPS] = 'psk:PageOutputColor'; + + /** + * Regular expression that matches a color option. + * @type {!RegExp} + * @const + */ + ColorCapability.COLOR_REGEX = /(.*color.*|.*rgb.*|.*cmy.*|true)/i; + + /** + * Regular expression that matches a black-white option. + * @type {!RegExp} + * @const + */ + ColorCapability.BW_REGEX = /(.*gray.*|.*mono.*|.*black.*|false)/i; + + ColorCapability.prototype = { + __proto__: CloudCapability.prototype, + + /** @return {string} Identifier of the color option. */ + get colorOption() { + return this.colorOption_; + }, + + /** @return {string} Identifier of the black-white option. */ + get bwOption() { + return this.bwOption_; + }, + + /** @return {boolean} Whether to print in color by default. */ + get isColorDefault() { + return this.isColorDefault_; + } + }; + + /** + * Cloud-based copies print capability. + * @param {string} id Identifier of the copies capability. + * @constructor + */ + function CopiesCapability(id) { + CloudCapability.call(this, id, CloudCapability.Type.PARAMETER_DEF); + }; + + CopiesCapability.prototype = { + __proto__: CloudCapability.prototype + }; + + /** + * Mapping of capability formats to an identifier of the copies capability. + * @type {object<CloudCapabilities.Format, string>} + */ + CopiesCapability.Id = {}; + CopiesCapability.Id[CloudCapabilities.Format.XPS] = + 'psk:JobCopiesAllDocuments'; + + /** + * Cloud-based duplex print capability. + * @param {string} id Identifier of the duplex capability. + * @param {string} simplexOption Identifier of the no-duplexing option. + * @param {string} longEdgeOption Identifier of the duplex on long edge + * option. + * @param {boolean} Whether duplexing is enabled by default. + * @constructor + */ + function DuplexCapability( + id, simplexOption, longEdgeOption, isDuplexDefault) { + CloudCapability.call(this, id, CloudCapability.Type.FEATURE); + + /** + * Identifier of the no-duplexing option. + * @type {string} + * @private + */ + this.simplexOption_ = simplexOption; + + /** + * Identifier of the duplex on long edge option. + * @type {string} + * @private + */ + this.longEdgeOption_ = longEdgeOption; + + /** + * Whether duplexing is enabled by default. + * @type {boolean} + * @private + */ + this.isDuplexDefault_ = isDuplexDefault; + }; + + /** + * Mapping of capability formats to an identifier of the duplex capability. + * @type {object<CloudCapabilities.Format, string>} + */ + DuplexCapability.Id = {}; + DuplexCapability.Id[CloudCapabilities.Format.PPD] = 'Duplex'; + DuplexCapability.Id[CloudCapabilities.Format.XPS] = + 'psk:JobDuplexAllDocumentsContiguously'; + + /** + * Regular expression that matches a no-duplexing option. + * @type {!RegExp} + * @const + */ + DuplexCapability.SIMPLEX_REGEX = /(.*onesided.*|.*none.*)/i; + + /** + * Regular expression that matches a duplex on long edge option. + * @type {!RegExp} + * @const + */ + DuplexCapability.LONG_EDGE_REGEX = /(.*longedge.*|duplexNoTumble)/i; + + DuplexCapability.prototype = { + __proto__: CloudCapability.prototype, + + /** @return {string} Identifier of the no-duplexing option. */ + get simplexOption() { + return this.simplexOption_; + }, + + /** @return {string} Identifier of the duplex on long edge option. */ + get longEdgeOption() { + return this.longEdgeOption_; + }, + + /** @return {boolean} Whether duplexing is enabled by default. */ + get isDuplexDefault() { + return this.isDuplexDefault_; + } + }; + + // Export + return { + CloudCapabilities: CloudCapabilities, + CollateCapability: CollateCapability, + ColorCapability: ColorCapability, + CopiesCapability: CopiesCapability, + DuplexCapability: DuplexCapability + }; +}); diff --git a/chrome/browser/resources/print_preview/data/cloud_parsers.js b/chrome/browser/resources/print_preview/data/cloud_parsers.js new file mode 100644 index 0000000..965e881 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/cloud_parsers.js @@ -0,0 +1,207 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('cloudprint', function() { + 'use strict'; + + /** Namespace which contains a method to parse cloud destinations directly. */ + function CloudDestinationParser() {}; + + /** + * Enumeration of cloud destination field names. + * @enum {string} + * @private + */ + CloudDestinationParser.Field_ = { + CAPABILITIES: 'capabilities', + DISPLAY_NAME: 'displayName', + FORMAT: 'capsFormat', + ID: 'id', + TAGS: 'tags' + }; + + /** + * Special tag that denotes whether the destination has been recently used. + * @type {string} + * @private + */ + CloudDestinationParser.RECENT_TAG_ = '^recent'; + + /** + * Parses a destination from JSON from a Google Cloud Print search or printer + * response. + * @param {object} json Object that represents a Google Cloud Print search or + * printer response. + * @return {!print_preview.Destination} Parsed destination. + */ + CloudDestinationParser.parse = function(json) { + if (!json.hasOwnProperty(CloudDestinationParser.Field_.ID) || + !json.hasOwnProperty(CloudDestinationParser.Field_.DISPLAY_NAME)) { + throw Error('Cloud destination does not have an ID or a display name'); + } + var isRecent = arrayContains( + json[CloudDestinationParser.Field_.TAGS] || [], + CloudDestinationParser.RECENT_TAG_); + var cloudDest = new print_preview.Destination( + json[CloudDestinationParser.Field_.ID], + json[CloudDestinationParser.Field_.DISPLAY_NAME], + isRecent, + false /*isLocal*/, + json[CloudDestinationParser.Field_.TAGS] || []); + if (json.hasOwnProperty(CloudDestinationParser.Field_.CAPABILITIES) && + json.hasOwnProperty(CloudDestinationParser.Field_.FORMAT)) { + cloudDest.capabilities = CloudCapabilitiesParser.parse( + json[CloudDestinationParser.Field_.FORMAT], + json[CloudDestinationParser.Field_.CAPABILITIES]); + } + return cloudDest; + }; + + /** + * Namespace which contains a method to parse a cloud destination's print + * capabilities. + */ + function CloudCapabilitiesParser() {}; + + /** + * Enumeration of cloud destination print capabilities field names. + * @enum {string} + * @private + */ + CloudCapabilitiesParser.Field_ = { + CAP_ID: 'name', + DEFAULT: 'default', + IS_DEFAULT: 'default', + OPTIONS: 'options', + OPTION_ID: 'name' + }; + + /** + * Parses print capabilities from an object in a given capabilities format. + * @param {print_preview.CloudCapabilities.Format} capsFormat Format of the + * printer capabilities. + * @param {object} json Object representing the cloud capabilities. + * @return {!print_preview.CloudCapabilities} Parsed print capabilities. + */ + CloudCapabilitiesParser.parse = function(capsFormat, json) { + var colorCapability = null; + var duplexCapability = null; + var copiesCapability = null; + var collateCapability = null; + for (var cap, i = 0; cap = json[i]; i++) { + var capId = cap[CloudCapabilitiesParser.Field_.CAP_ID]; + if (capId == print_preview.CollateCapability.Id[capsFormat]) { + collateCapability = CloudCapabilitiesParser.parseCollate(capId, cap); + } else if (capId == print_preview.ColorCapability.Id[capsFormat]) { + colorCapability = CloudCapabilitiesParser.parseColor(capId, cap); + } else if (capId == print_preview.CopiesCapability.Id[capsFormat]) { + copiesCapability = new print_preview.CopiesCapability(capId); + } else if (capId == print_preview.DuplexCapability.Id[capsFormat]) { + duplexCapability = CloudCapabilitiesParser.parseDuplex(capId, cap); + } + } + return new print_preview.CloudCapabilities( + collateCapability, colorCapability, copiesCapability, duplexCapability); + }; + + /** + * Parses a collate capability from the given object. + * @param {string} capId Native ID of the given capability object. + * @param {object} Object that represents the collate capability. + * @return {print_preview.CollateCapability} Parsed collate capability or + * {@code null} if the given capability object was not a valid collate + * capability. + */ + CloudCapabilitiesParser.parseCollate = function(capId, cap) { + var options = cap[CloudCapabilitiesParser.Field_.OPTIONS]; + var collateOption = null; + var noCollateOption = null; + var isCollateDefault = false; + for (var option, i = 0; option = options[i]; i++) { + var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID]; + if (!collateOption && + print_preview.CollateCapability.COLLATE_REGEX.test(optionId)) { + collateOption = optionId; + isCollateDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT]; + } else if (!noCollateOption && + print_preview.CollateCapability.NO_COLLATE_REGEX.test(optionId)) { + noCollateOption = optionId; + } + } + if (!collateOption || !noCollateOption) { + return null; + } + return new print_preview.CollateCapability( + capId, collateOption, noCollateOption, isCollateDefault); + }; + + /** + * Parses a color capability from the given object. + * @param {string} capId Native ID of the given capability object. + * @param {object} Object that represents the color capability. + * @return {print_preview.ColorCapability} Parsed color capability or + * {@code null} if the given capability object was not a valid color + * capability. + */ + CloudCapabilitiesParser.parseColor = function(capId, cap) { + var options = cap[CloudCapabilitiesParser.Field_.OPTIONS]; + var colorOption = null; + var bwOption = null; + var isColorDefault = false; + for (var option, i = 0; option = options[i]; i++) { + var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID]; + if (!colorOption && + print_preview.ColorCapability.COLOR_REGEX.test(optionId)) { + colorOption = optionId; + isColorDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT]; + } else if (!bwOption && + print_preview.ColorCapability.BW_REGEX.test(optionId)) { + bwOption = optionId; + } + } + if (!colorOption || !bwOption) { + return null; + } + return new print_preview.ColorCapability( + capId, colorOption, bwOption, isColorDefault); + }; + + /** + * Parses a duplex capability from the given object. + * @param {string} capId Native ID of the given capability object. + * @param {object} Object that represents the duplex capability. + * @return {print_preview.DuplexCapability} Parsed duplex capability or + * {@code null} if the given capability object was not a valid duplex + * capability. + */ + CloudCapabilitiesParser.parseDuplex = function(capId, cap) { + var options = cap[CloudCapabilitiesParser.Field_.OPTIONS]; + var simplexOption = null; + var longEdgeOption = null; + var isDuplexDefault = false; + for (var option, i = 0; option = options[i]; i++) { + var optionId = option[CloudCapabilitiesParser.Field_.OPTION_ID]; + if (!simplexOption && + print_preview.DuplexCapability.SIMPLEX_REGEX.test(optionId)) { + simplexOption = optionId; + } else if (!longEdgeOption && + print_preview.DuplexCapability.LONG_EDGE_REGEX.test(optionId)) { + longEdgeOption = optionId; + isDuplexDefault = !!option[CloudCapabilitiesParser.Field_.DEFAULT]; + } + } + if (!simplexOption || !longEdgeOption) { + return null; + } + return new print_preview.DuplexCapability( + capId, simplexOption, longEdgeOption, isDuplexDefault); + }; + + // Export + return { + CloudCapabilitiesParser: CloudCapabilitiesParser, + CloudDestinationParser: CloudDestinationParser + }; +}); + diff --git a/chrome/browser/resources/print_preview/data/coordinate2d.js b/chrome/browser/resources/print_preview/data/coordinate2d.js new file mode 100644 index 0000000..4b10af6 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/coordinate2d.js @@ -0,0 +1,76 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Immutable two dimensional point in space. The units of the dimensions are + * undefined. + * @param {number} x X-dimension of the point. + * @param {number} y Y-dimension of the point. + * @constructor + */ + function Coordinate2d(x, y) { + /** + * X-dimension of the point. + * @type {number} + * @private + */ + this.x_ = x; + + /** + * Y-dimension of the point. + * @type {number} + * @private + */ + this.y_ = y; + }; + + Coordinate2d.prototype = { + /** @return {number} X-dimension of the point. */ + get x() { + return this.x_; + }, + + /** @return {number} Y-dimension of the point. */ + get y() { + return this.y_; + }, + + /** + * @param {number} x Amount to translate in the X dimension. + * @param {number} y Amount to translate in the Y dimension. + * @return {!print_preview.Coordinate2d} A new two-dimensional point + * translated along the X and Y dimensions. + */ + translate: function(x, y) { + return new Coordinate2d(this.x_ + x, this.y_ + y); + }, + + /** + * @param {number} factor Amount to scale the X and Y dimensions. + * @return {!print_preview.Coordinate2d} A new two-dimensional point scaled + * by the given factor. + */ + scale: function(factor) { + return new Coordinate2d(this.x_ * factor, this.y_ * factor); + }, + + /** + * @param {print_preview.Coordinate2d} other The point to compare against. + * @return {boolean} Whether another point is equal to this one. + */ + equals: function(other) { + return other != null && + this.x_ == other.x_ && + this.y_ == other.y_; + } + }; + + // Export + return { + Coordinate2d: Coordinate2d + }; +}); diff --git a/chrome/browser/resources/print_preview/data/destination.js b/chrome/browser/resources/print_preview/data/destination.js new file mode 100644 index 0000000..01a9491 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/destination.js @@ -0,0 +1,188 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Print destination data object that holds data for both local and cloud + * destinations. + * @param {string} id ID of the destination. + * @param {string} displayName Display name of the destination. + * @param {boolean} isRecent Whether the destination has been used recently. + * @param {boolean} isLocal Whether the destination is local or cloud-based. + * @param {Array.<string>=} opt_tags Tags associated with the destination. + * @constructor + */ + function Destination(id, displayName, isRecent, isLocal, opt_tags) { + /** + * ID of the destination. + * @type {string} + * @private + */ + this.id_ = id; + + /** + * Display name of the destination. + * @type {string} + * @private + */ + this.displayName_ = displayName; + + /** + * Whether the destination has been used recently. + * @type {boolean} + * @private + */ + this.isRecent_ = isRecent; + + /** + * Whether the destination is local or cloud-based. + * @type {boolean} + * @private + */ + this.isLocal_ = isLocal; + + /** + * Tags associated with the destination. + * @type {!Array.<string>} + * @private + */ + this.tags_ = opt_tags || []; + + /** + * Print capabilities of the destination. + * @type {print_preview.ChromiumCapabilities} + * @private + */ + this.capabilities_ = null; + + /** + * Cache of destination location fetched from tags. + * @type {string} + * @private + */ + this.location_ = null; + }; + + /** + * Prefix of the location destination tag. + * @type {string} + * @const + */ + Destination.LOCATION_TAG_PREFIX = '__cp__printer-location='; + + /** + * Enumeration of Google-promoted destination IDs. + * @enum {string} + */ + Destination.GooglePromotedId = { + DOCS: '__google__docs', + SAVE_AS_PDF: 'Save as PDF', + PRINT_WITH_CLOUD_PRINT: 'printWithCloudPrint' + }; + + Destination.prototype = { + /** @return {string} ID of the destination. */ + get id() { + return this.id_; + }, + + /** @return {string} Display name of the destination. */ + get displayName() { + return this.displayName_; + }, + + /** @return {boolean} Whether the destination has been used recently. */ + get isRecent() { + return this.isRecent_; + }, + + /** + * @param {boolean} isRecent Whether the destination has been used recently. + */ + set isRecent(isRecent) { + this.isRecent_ = isRecent; + }, + + /** @return {boolean} Whether the destination is local or cloud-based. */ + get isLocal() { + return this.isLocal_; + }, + + /** @return {boolean} Whether the destination is promoted by Google. */ + get isGooglePromoted() { + for (var key in Destination.GooglePromotedId) { + if (Destination.GooglePromotedId[key] == this.id_) { + return true; + } + } + return false; + }, + + /** + * @return {boolean} Whether the destination is the "Print with Cloud Print" + * destination. + */ + get isPrintWithCloudPrint() { + return this.id_ == Destination.GooglePromotedId.PRINT_WITH_CLOUD_PRINT; + }, + + /** + * @return {string} The location of the destination, or an empty string if + * the location is unknown. + */ + get location() { + if (this.location_ == null) { + for (var tag, i = 0; tag = this.tags_[i]; i++) { + if (tag.indexOf(Destination.LOCATION_TAG_PREFIX) == 0) { + this.location_ = tag.substring( + Destination.LOCATION_TAG_PREFIX.length); + } + } + if (this.location_ == null) { + this.location_ = ''; + } + } + return this.location_; + }, + + /** @return {!Array.<string>} Tags associated with the destination. */ + get tags() { + return this.tags_.slice(0); + }, + + /** + * @return {print_preview.ChromiumCapabilities} Print capabilities of the + * destination. + */ + get capabilities() { + return this.capabilities_; + }, + + /** + * @param {!print_preview.ChromiumCapabilities} capabilities Print + * capabilities of the destination. + */ + set capabilities(capabilities) { + this.capabilities_ = capabilities; + }, + + /** + * Matches a query against the destination. + * @param {string} query Query to match against the destination. + * @return {boolean} {@code true} if the query matches this destination, + * {@code false} otherwise. + */ + matches: function(query) { + return this.displayName_.toLowerCase().indexOf( + query.toLowerCase().trim()) != -1; + } + }; + + // Export + return { + Destination: Destination + }; +}); diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js new file mode 100644 index 0000000..69f766a --- /dev/null +++ b/chrome/browser/resources/print_preview/data/destination_store.js @@ -0,0 +1,192 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * A data store that stores destinations and dispatches events when the data + * store changes. + * @constructor + * @extends {cr.EventTarget} + */ + function DestinationStore() { + cr.EventTarget.call(this); + + /** + * Internal backing store for the data store. + * @type {!Array.<print_preview.Destination>} + * @private + */ + this.destinations_ = []; + + /** + * Currently selected destination. + * @type {print_preview.Destination} + * @private + */ + this.selectedDestination_ = null; + + /** + * Initial destination ID used to auto-select the first inserted destination + * that matches. If {@code null}, the first destination inserted into the + * store will be selected. + * @type {?string} + * @private + */ + this.initialDestinationId_ = null; + + /** + * Whether the destination store will auto select the destination that + * matches the initial destination. + * @type {boolean} + * @private + */ + this.isInAutoSelectMode_ = false; + }; + + /** + * Event types dispatched by the data store. + * @enum {string} + */ + DestinationStore.EventType = { + DESTINATIONS_INSERTED: + 'print_preview.DestinationStore.DESTINATIONS_INSERTED', + DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT' + }; + + DestinationStore.prototype = { + __proto__: cr.EventTarget.prototype, + + /** + * @return {!Array.<!print_preview.Destination>} List of destinations in + * the store. + */ + get destinations() { + return this.destinations_.slice(0); + }, + + /** + * @return {print_preview.Destination} The currently selected destination or + * {@code null} if none is selected. + */ + get selectedDestination() { + return this.selectedDestination_; + }, + + /** + * Sets the initially selected destination. If any inserted destinations + * match this ID, that destination will be automatically selected. This + * occurs only once for every time this setter is called or if the store is + * cleared. + * @param {string} ID of the destination that should be selected + * automatically when added to the store. + */ + setInitialDestinationId: function(initialDestinationId) { + this.initialDestinationId_ = initialDestinationId; + this.isInAutoSelectMode_ = true; + if (this.initialDestinationId_ == null && this.destinations_.length > 0) { + this.selectDestination(this.destinations_[0]); + } else if (this.initialDestinationId_ != null) { + for (var dest, i = 0; dest = this.destinations_[i]; i++) { + if (dest.id == initialDestinationId) { + this.selectDestination(dest); + break; + } + } + } + }, + + /** @param {!print_preview.Destination} Destination to select. */ + selectDestination: function(destination) { + this.selectedDestination_ = destination; + this.selectedDestination_.isRecent = true; + this.isInAutoSelectMode_ = false; + cr.dispatchSimpleEvent( + this, DestinationStore.EventType.DESTINATION_SELECT); + }, + + /** + * Inserts a print destination to the data store and dispatches a + * DESTINATIONS_INSERTED event. If the destination matches the initial + * destination ID, then the destination will be automatically selected. + * @param {!print_preview.Destination} destination Print destination to + * insert. + */ + insertDestination: function(destination) { + this.destinations_.push(destination); + cr.dispatchSimpleEvent( + this, DestinationStore.EventType.DESTINATIONS_INSERTED); + if (this.isInAutoSelectMode_) { + if (this.initialDestinationId_ == null) { + this.selectDestination(destination); + } else { + if (destination.id == this.initialDestinationId_) { + this.selectDestination(destination); + } + } + } + }, + + /** + * Inserts multiple print destinations to the data store and dispatches one + * DESTINATIONS_INSERTED event. If any of the destinations match the initial + * destination ID, then that destination will be automatically selected. + * @param {!Array.<print_preview.Destination>} destinations Print + * destinations to insert. + */ + insertDestinations: function(destinations) { + this.destinations_ = this.destinations_.concat(destinations); + cr.dispatchSimpleEvent( + this, DestinationStore.EventType.DESTINATIONS_INSERTED); + if (this.isInAutoSelectMode_) { + if (this.initialDestinationId_ == null && destinations.length > 0) { + this.selectDestination(destinations[0]); + } else if (this.initialDestinationId_ != null) { + for (var dest, i = 0; dest = destinations[i]; i++) { + if (dest.id == this.initialDestinationId_) { + this.selectDestination(dest); + break; + } + } + } + } + }, + + /** + * Updates an existing print destination with capabilities information. If + * the destination doesn't already exist, it will be added. + * @param {!print_preview.Destination} destination Destination to update. + * @return {!print_preview.Destination} The existing destination that was + * updated. + */ + updateDestination: function(destination) { + var existingDestination = null; + for (var d, i = 0; d = this.destinations_[i]; i++) { + if (destination.id == d.id) { + existingDestination = d; + break; + } + } + if (existingDestination) { + existingDestination.capabilities = destination.capabilities; + return existingDestination; + } else { + this.insertDestination(destination); + } + }, + + /** Clears all print destinations. */ + clear: function() { + this.destinations_ = []; + this.selectedDestination_ = null; + this.isInAutoSelectMode_ = true; + } + }; + + // Export + return { + DestinationStore: DestinationStore + }; +}); diff --git a/chrome/browser/resources/print_preview/data/document_info.js b/chrome/browser/resources/print_preview/data/document_info.js new file mode 100644 index 0000000..dd2fdcd --- /dev/null +++ b/chrome/browser/resources/print_preview/data/document_info.js @@ -0,0 +1,55 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Object which contains information related to the document to print. + * @constructor + */ + function DocumentInfo() { + /** + * Whether the document to print is modifiable (i.e. can be reflowed). + * @type {boolean} + */ + this.isModifiable = true; + + /** + * Number of pages in the document to print. + * @type {number} + */ + this.pageCount = 1; + + /** + * Size of the pages of the document in points. + * @type {!print_preview.Size} + */ + this.pageSize = new print_preview.Size(0, 0); + + /** + * Printable area of the document in points. + * @type {!print_preview.PrintableArea} + */ + this.printableArea = new print_preview.PrintableArea( + new print_preview.Coordinate2d(0, 0), new print_preview.Size(0, 0)); + + /** + * Whether the document is styled by CSS media styles. + * @type {boolean} + */ + this.hasCssMediaStyles = false; + + /** + * Margins of the document in points. + * @type {print_preview.Margins} + */ + this.margins = null; + }; + + // Export + return { + DocumentInfo: DocumentInfo + }; +}); diff --git a/chrome/browser/resources/print_preview/data/local_parsers.js b/chrome/browser/resources/print_preview/data/local_parsers.js new file mode 100644 index 0000000..af58290 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/local_parsers.js @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** Namespace that contains a method to parse local print destinations. */ + function LocalDestinationParser() {}; + + /** + * Parses a local print destination. + * @param {object} destinationInfo Information describing a local print + * destination. + * @return {!print_preview.Destination} Parsed local print destination. + */ + LocalDestinationParser.parse = function(destinationInfo) { + return new print_preview.Destination( + destinationInfo.deviceName, + destinationInfo.printerName, + false /*isRecent*/, + true /*isLocal*/); + }; + + /** Namespace that contains a method to parse local print capabilities. */ + function LocalCapabilitiesParser() {}; + + /** + * Parses local print capabilities. + * @param {object} settingsInfo Object that describes local print + * capabilities. + * @return {!print_preview.ChromiumCapabilities} Parsed local print + * capabilities. + */ + LocalCapabilitiesParser.parse = function(settingsInfo) { + var hasColorCapability = false; + var defaultIsColorEnabled = false; + if (hasColorCapability = !settingsInfo['disableColorOption']) { + defaultIsColorEnabled = settingsInfo['setColorAsDefault']; + } + + var hasDuplexCapability = false; + var defaultIsDuplexEnabled = false; + if (hasDuplexCapability = + settingsInfo['printerDefaultDuplexValue'] != + print_preview.NativeLayer.DuplexMode.UNKNOWN_DUPLEX_MODE) { + defaultIsDuplexEnabled = + settingsInfo['printerDefaultDuplexValue'] == + print_preview.NativeLayer.DuplexMode.LONG_EDGE; + } + + return new print_preview.ChromiumCapabilities( + !settingsInfo['disableCopiesOption'] /*hasCopiesCapability*/, + '1' /*defaultCopiesStr*/, + true /*hasCollateCapability*/, + true /*defaultIsCollateEnabled*/, + hasDuplexCapability, + defaultIsDuplexEnabled, + !settingsInfo['disableLandscapeOption'] /*hasOrientationCapability*/, + false /*defaultIsLandscapeEnabled*/, + hasColorCapability, + defaultIsColorEnabled); + }; + + // Export + return { + LocalCapabilitiesParser: LocalCapabilitiesParser, + LocalDestinationParser: LocalDestinationParser + }; +}); diff --git a/chrome/browser/resources/print_preview/data/margins.js b/chrome/browser/resources/print_preview/data/margins.js new file mode 100644 index 0000000..cf6a6f8 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/margins.js @@ -0,0 +1,86 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a Margins object that holds four margin values in points. + * @param {number} top The top margin in pts. + * @param {number} right The right margin in pts. + * @param {number} bottom The bottom margin in pts. + * @param {number} left The left margin in pts. + * @constructor + */ + function Margins(top, right, bottom, left) { + /** + * Backing store for the margin values in points. + * @type {Object.< + * print_preview.ticket_items.CustomMargins.Orientation, + * number>} + * @private + */ + this.value_ = {}; + this.value_[print_preview.ticket_items.CustomMargins.Orientation.TOP] = top; + this.value_[print_preview.ticket_items.CustomMargins.Orientation.RIGHT] = + right; + this.value_[print_preview.ticket_items.CustomMargins.Orientation.BOTTOM] = + bottom; + this.value_[print_preview.ticket_items.CustomMargins.Orientation.LEFT] = + left; + }; + + Margins.prototype = { + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin value to get. + * @return {number} Value of the margin of the given orientation. + */ + get: function(orientation) { + return this.value_[orientation]; + }, + + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin to set. + * @param {number} value Updated value of the margin in points to modify. + * @return {!print_preview.Margins} A new copy of |this| with the + * modification made to the specified margin. + */ + set: function(orientation, value) { + var newValue = {}; + for (var o in this.value_) { + newValue[o] = this.value_[o]; + } + newValue[orientation] = value; + return new Margins( + newValue[print_preview.ticket_items.CustomMargins.Orientation.TOP], + newValue[print_preview.ticket_items.CustomMargins.Orientation.RIGHT], + newValue[print_preview.ticket_items.CustomMargins.Orientation.BOTTOM], + newValue[print_preview.ticket_items.CustomMargins.Orientation.LEFT]); + }, + + /** + * @param {print_preview.Margins} other The other margins object to compare + * against. + * @return {boolean} Whether this margins object is equal to another. + */ + equals: function(other) { + if (other == null) { + return false; + } + for (var orientation in this.value_) { + if (this.value_[orientation] != other.value_[orientation]) { + return false; + } + } + return true; + } + }; + + // Export + return { + Margins: Margins + }; +}); diff --git a/chrome/browser/resources/print_preview/data/measurement_system.js b/chrome/browser/resources/print_preview/data/measurement_system.js new file mode 100644 index 0000000..ccee0d9a --- /dev/null +++ b/chrome/browser/resources/print_preview/data/measurement_system.js @@ -0,0 +1,159 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Measurement system of the print preview. Used to parse and serialize point + * measurements into the system's local units (e.g. millimeters, inches). + * @param {string} thousandsDelimeter Delimeter between thousands digits. + * @param {string} decimalDelimeter Delimeter between integers and decimals. + * @param {print_preview.MeasurementSystem.UnitType} unitType Measurement unit + * type of the system. + * @constructor + */ + function MeasurementSystem(thousandsDelimeter, decimalDelimeter, unitType) { + this.thousandsDelimeter_ = thousandsDelimeter || ','; + this.decimalDelimeter_ = decimalDelimeter || '.'; + this.unitType_ = unitType; + }; + + /** + * Parses |numberFormat| and extracts the symbols used for the thousands point + * and decimal point. + * @param {string} numberFormat The formatted version of the number 12345678. + * @return {!Array.<string>} The extracted symbols in the order + * [thousandsSymbol, decimalSymbol]. For example, + * parseNumberFormat("123,456.78") returns [",", "."]. + */ + MeasurementSystem.parseNumberFormat = function(numberFormat) { + if (!numberFormat) { + return [',', '.']; + } + var regex = /^(\d+)(\W?)(\d+)(\W?)(\d+)$/; + var matches = numberFormat.match(regex) || ['', '', ',', '', '.']; + return [matches[2], matches[4]]; + }; + + /** + * Enumeration of measurement unit types. + * @enum {number} + */ + MeasurementSystem.UnitType = { + METRIC: 0, // millimeters + IMPERIAL: 1 // inches + }; + + /** + * Maximum resolution of local unit values. + * @type {Object.<print_preview.MeasurementSystem.UnitType, number>} + * @private + */ + MeasurementSystem.Precision_ = {}; + MeasurementSystem.Precision_[MeasurementSystem.UnitType.METRIC] = 0.5; + MeasurementSystem.Precision_[MeasurementSystem.UnitType.IMPERIAL] = 0.01; + + /** + * Maximum number of decimal places to keep for local unit. + * @type {Object.<print_preview.MeasurementSystem.UnitType, number>} + * @private + */ + MeasurementSystem.DecimalPlaces_ = {}; + MeasurementSystem.DecimalPlaces_[MeasurementSystem.UnitType.METRIC] = 1; + MeasurementSystem.DecimalPlaces_[MeasurementSystem.UnitType.IMPERIAL] = 2; + + /** + * Number of points per inch. + * @type {number} + * @const + * @private + */ + MeasurementSystem.PTS_PER_INCH_ = 72.0; + + /** + * Number of points per millimeter. + * @type {number} + * @const + * @private + */ + MeasurementSystem.PTS_PER_MM_ = MeasurementSystem.PTS_PER_INCH_ / 25.4; + + MeasurementSystem.prototype = { + /** @return {string} The unit type symbol of the measurement system. */ + get unitSymbol() { + if (this.unitType_ == MeasurementSystem.UnitType.METRIC) { + return 'mm'; + } else if (this.unitType_ == MeasurementSystem.UnitType.IMPERIAL) { + return '"'; + } else { + throw Error('Unit type not supported: ' + this.unitType_); + } + }, + + /** + * @return {string} The thousands delimeter character of the measurement + * system. + */ + get thousandsDelimeter() { + return this.thousandsDelimeter_; + }, + + /** + * @return {string} The decimal delimeter character of the measurement + * system. + */ + get decimalDelimeter() { + return this.decimalDelimeter_; + }, + + setSystem: function(thousandsDelimeter, decimalDelimeter, unitType) { + this.thousandsDelimeter_ = thousandsDelimeter; + this.decimalDelimeter_ = decimalDelimeter; + this.unitType_ = unitType; + }, + + /** + * Rounds a value in the local system's units to the appropriate precision. + * @param {number} value Value to round. + * @return {number} Rounded value. + */ + roundValue: function(value) { + var precision = MeasurementSystem.Precision_[this.unitType_]; + var roundedValue = Math.round(value / precision) * precision; + // Truncate + return roundedValue.toFixed( + MeasurementSystem.DecimalPlaces_[this.unitType_]); + }, + + /** + * @param {number} pts Value in points to convert to local units. + * @return {number} Value in local units. + */ + convertFromPoints: function(pts) { + if (this.unitType_ == MeasurementSystem.UnitType.METRIC) { + return pts / MeasurementSystem.PTS_PER_MM_; + } else { + return pts / MeasurementSystem.PTS_PER_INCH_; + } + }, + + /** + * @param {number} Value in local units to convert to points. + * @return {number} Value in points. + */ + convertToPoints: function(localUnits) { + if (this.unitType_ == MeasurementSystem.UnitType.METRIC) { + return localUnits * MeasurementSystem.PTS_PER_MM_; + } else { + return localUnits * MeasurementSystem.PTS_PER_INCH_; + } + } + }; + + // Export + return { + MeasurementSystem: MeasurementSystem + }; +}); diff --git a/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs b/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs new file mode 100644 index 0000000..ed246c8 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/measurement_system_unittest.gtestjs @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * Test fixture for the MeasurementSystem. + * @constructor + * @extends {testing.Test} + */ +function MeasurementSystemUnitTest() { + testing.Test.call(this); +} + +MeasurementSystemUnitTest.prototype = { + __proto__: testing.Test.prototype, + + extraLibraries: [ + '../../shared/js/cr.js', + '../print_preview_utils.js', + 'measurement_system.js' + ] +}; + +TEST_F('MeasurementSystemUnitTest', 'parseNumberFormat', function() { + assertTrue(areArraysEqual( + ['.', ','], + print_preview.MeasurementSystem.parseNumberFormat('123.456,78'))); + assertTrue(areArraysEqual( + ['.', '.'], + print_preview.MeasurementSystem.parseNumberFormat('123.456.78'))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat('123,456.78'))); + assertTrue(areArraysEqual( + [',', ','], + print_preview.MeasurementSystem.parseNumberFormat('123,456,78'))); + assertTrue(areArraysEqual( + [' ', ','], + print_preview.MeasurementSystem.parseNumberFormat('123 456,78'))); + assertTrue(areArraysEqual( + [' ', '.'], + print_preview.MeasurementSystem.parseNumberFormat('123 456.78'))); + assertTrue(areArraysEqual( + [' ', ' '], + print_preview.MeasurementSystem.parseNumberFormat('123 456 78'))); + assertTrue(areArraysEqual( + ['', ''], + print_preview.MeasurementSystem.parseNumberFormat('123'))); + + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat('abcdef'))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat(null))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat(undefined))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat(''))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat('1'))); + assertTrue(areArraysEqual( + [',', '.'], + print_preview.MeasurementSystem.parseNumberFormat('12'))); +}); diff --git a/chrome/browser/resources/print_preview/data/page_number_set.js b/chrome/browser/resources/print_preview/data/page_number_set.js new file mode 100644 index 0000000..cccb6bf --- /dev/null +++ b/chrome/browser/resources/print_preview/data/page_number_set.js @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * An immutable ordered set of page numbers. + * @param {!Array.<number>} pageNumberList A list of page numbers to include + * in the set. + * @constructor + */ + function PageNumberSet(pageNumberList) { + /** + * Internal data store for the page number set. + * @type {!Array.<number>} + * @private + */ + this.pageNumberSet_ = pageListToPageSet(pageNumberList); + }; + + /** + * @param {string} pageRangeStr String form of a page range. I.e. '2,3,4-5'. + * If string is empty, all page numbers will be in the page number set. + * @param {number} totalPageCount Total number of pages in the original + * document. + * @return {print_preview.PageNumberSet} Page number set parsed from the + * given page range string and total page count. Null returned if + * the given page range string is invalid. + */ + PageNumberSet.parse = function(pageRangeStr, totalPageCount) { + if (pageRangeStr == '') { + var pageNumberList = []; + for (var i = 0; i < totalPageCount; i++) { + pageNumberList.push(i + 1); + } + return new PageNumberSet(pageNumberList); + } else { + return isPageRangeTextValid(pageRangeStr, totalPageCount) ? + new PageNumberSet( + pageRangeTextToPageList(pageRangeStr, totalPageCount)) : null; + } + }; + + PageNumberSet.prototype = { + /** @return {number} The number of page numbers in the set. */ + get size() { + return this.pageNumberSet_.length; + }, + + /** + * @param {number} index 0-based index of the page number to get. + * @return {number} Page number at the given index. + */ + getPageNumberAt: function(index) { + return this.pageNumberSet_[index]; + }, + + /** + * @param {number} 1-based page number to check for. + * @return {boolean} Whether the given page number is in the page range. + */ + hasPageNumber: function(pageNumber) { + return arrayContains(this.pageNumberSet_, pageNumber); + }, + + /** + * @param {number} 1-based number of the page to get index of. + * @return {number} 0-based index of the given page number with respect to + * all of the pages in the page range. + */ + getPageNumberIndex: function(pageNumber) { + return this.pageNumberSet_.indexOf(pageNumber); + }, + + /** + * @return {!Array.<object.<{from: number, to: number}>>} A list of page + * ranges suitable for use in the native layer. + */ + getPageRanges: function() { + return pageSetToPageRanges(this.pageNumberSet_); + }, + + /** @return {!Array.<number>} Array representation of the set. */ + asArray: function() { + return this.pageNumberSet_.slice(0); + }, + + /** + * @param {print_preview.PageNumberSet} other Page number set to compare + * against. + * @return {boolean} Whether another page number set is equal to this one. + */ + equals: function(other) { + return other == null ? + false : areArraysEqual(this.pageNumberSet_, other.pageNumberSet_); + } + }; + + // Export + return { + PageNumberSet: PageNumberSet + }; +}); diff --git a/chrome/browser/resources/print_preview/data/print_ticket_store.js b/chrome/browser/resources/print_preview/data/print_ticket_store.js new file mode 100644 index 0000000..e200798 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/print_ticket_store.js @@ -0,0 +1,629 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + // TODO(rltoscano): Maybe clear print ticket when destination changes. Or + // better yet, carry over any print ticket state that is possible. I.e. if + // destination changes, the new destination might not support duplex anymore, + // so we should clear the ticket's isDuplexEnabled state. + + /** + * Storage of the print ticket and document statistics. Dispatches events when + * the contents of the print ticket or document statistics change. Also + * handles validation of the print ticket against destination capabilities and + * against the document. + * @param {!print_preview.DestinationStore} destinationStore Used to + * understand which printer is selected. + * @constructor + * @extends {cr.EventTarget} + */ + function PrintTicketStore(destinationStore) { + cr.EventTarget.call(this); + + /** + * Destination store used to understand which printer is selected. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + + // Create the document info with some initial settings. Actual + // page-related information won't be set until preview generation occurs, + // so we'll use some defaults until then. This way, the print ticket store + // will be valid even if no preview can be generated. + var initialPageSize = new print_preview.Size(612, 792); // 8.5"x11" + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = new print_preview.DocumentInfo(); + this.documentInfo_.isModifiable = true; + this.documentInfo_.pageCount = 1; + this.documentInfo_.pageSize = initialPageSize; + this.documentInfo_.printableArea = new print_preview.PrintableArea( + new print_preview.Coordinate2d(0, 0), initialPageSize); + + /** + * Printing capabilities of Chromium and the currently selected destination. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder(); + + /** + * Current measurement system. Used to work with margin measurements. + * @type {!print_preview.MeasurementSystem} + * @private + */ + this.measurementSystem_ = new print_preview.MeasurementSystem( + ',', '.', print_preview.MeasurementSystem.UnitType.IMPERIAL); + + /** + * Collate ticket item. + * @type {!print_preview.ticket_items.Collate} + * @private + */ + this.collate_ = + new print_preview.ticket_items.Collate(this.capabilitiesHolder_); + + /** + * Color ticket item. + * @type {!print_preview.ticket_items.Color} + * @private + */ + this.color_ = new print_preview.ticket_items.Color( + this.capabilitiesHolder_, this.destinationStore_); + + /** + * Copies ticket item. + * @type {!print_preview.ticket_items.Copies} + * @private + */ + this.copies_ = + new print_preview.ticket_items.Copies(this.capabilitiesHolder_); + + /** + * Duplex ticket item. + * @type {!print_preview.ticket_items.Duplex} + * @private + */ + this.duplex_ = + new print_preview.ticket_items.Duplex(this.capabilitiesHolder_); + + /** + * Landscape ticket item. + * @type {!print_preview.ticket_items.Landscape} + * @private + */ + this.landscape_ = new print_preview.ticket_items.Landscape( + this.capabilitiesHolder_, this.documentInfo_); + + /** + * Page range ticket item. + * @type {!print_preview.ticket_items.PageRange} + * @private + */ + this.pageRange_ = + new print_preview.ticket_items.PageRange(this.documentInfo_); + + /** + * Margins type ticket item. + * @type {!print_preview.ticket_items.MarginsType} + * @private + */ + this.marginsType_ = + new print_preview.ticket_items.MarginsType(this.documentInfo_); + + /** + * Custom margins ticket item. + * @type {!print_preview.ticket_items.CustomMargins} + * @private + */ + this.customMargins_ = new print_preview.ticket_items.CustomMargins( + this.documentInfo_, this.measurementSystem_); + + /** + * Header-footer ticket item. + * @type {!print_preview.ticket_items.HeaderFooter} + * @private + */ + this.headerFooter_ = new print_preview.ticket_items.HeaderFooter( + this.documentInfo_, this.marginsType_, this.customMargins_); + + /** + * Fit-to-page ticket item. + * @type {!print_preview.ticket_items.FitToPage} + * @private + */ + this.fitToPage_ = new print_preview.ticket_items.FitToPage( + this.documentInfo_, this.destinationStore_); + }; + + /** + * Event types dispatched by the print ticket store. + * @enum {string} + */ + PrintTicketStore.EventType = { + CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE', + DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE', + INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE', + TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE' + }; + + PrintTicketStore.prototype = { + __proto__: cr.EventTarget.prototype, + + /** @return {boolean} Whether the document is modifiable. */ + get isDocumentModifiable() { + return this.documentInfo_.isModifiable; + }, + + /** @return {number} Number of pages in the document. */ + get pageCount() { + return this.documentInfo_.pageCount; + }, + + /** + * @param {number} pageCount New number of pages in the document. + * Dispatches a DOCUMENT_CHANGE event if the value changes. + */ + updatePageCount: function(pageCount) { + if (this.documentInfo_.pageCount != pageCount) { + this.documentInfo_.pageCount = pageCount; + cr.dispatchSimpleEvent( + this, PrintTicketStore.EventType.DOCUMENT_CHANGE); + } + }, + + /** + * @return {!print_preview.PrintableArea} Printable area of the document in + * points. + */ + get printableArea() { + return this.documentInfo_.printableArea; + }, + + /** @return {!print_preview.Size} Size of the document in points. */ + get pageSize() { + return this.documentInfo_.pageSize; + }, + + /** + * Updates a subset of fields of the print document relating to the format + * of the page. + * @param {!print_preview.PrintableArea} printableArea New printable area of + * the document in points. Dispatches a DOCUMENT_CHANGE event if the + * value changes. + * @param {!print_preview.Size} pageSize New size of the document in points. + * Dispatches a DOCUMENT_CHANGE event if the value changes. + * @param {boolean} documentHasCssMediaStyles Whether the document is styled + * with CSS media styles. + * @param {!print_preview.Margins} margins Document margins in points. + */ + updateDocumentPageInfo: function( + printableArea, pageSize, documentHasCssMediaStyles, margins) { + if (!this.documentInfo_.printableArea.equals(printableArea) || + !this.documentInfo_.pageSize.equals(pageSize) || + this.documentInfo_.hasCssMediaStyles != documentHasCssMediaStyles || + this.documentInfo_.margins == null || + !this.documentInfo_.margins.equals(margins)) { + this.documentInfo_.printableArea = printableArea; + this.documentInfo_.pageSize = pageSize; + this.documentInfo_.hasCssMediaStyles = documentHasCssMediaStyles; + this.documentInfo_.margins = margins; + cr.dispatchSimpleEvent( + this, PrintTicketStore.EventType.DOCUMENT_CHANGE); + } + }, + + /** + * @return {!print_preview.MeasurementSystem} Measurement system of the + * local system. + */ + get measurementSystem() { + return this.measurementSystem_; + }, + + /** + * @return {print_preview.Margins} Document margins of the currently + * generated preview. + */ + getDocumentMargins: function() { + return this.documentInfo_.margins; + }, + + /** + * Initializes the print ticket store. Dispatches an INITIALIZE event. + * @param {boolean} isDocumentModifiable Whether the document to print is + * modifiable (i.e. can be re-flowed by Chromium). + * @param {?boolean} isDuplexEnabled Previous duplex setting. + * @param {?boolean} isHeaderFooterEnabled Previous header-footer setting. + * @param {?print_preview.ticket_items.MarginsType.Value} marginsType + * Previous margins type. + * @param {print_preview.Margins} customMargins Previous custom margins. + * @param {string} thousandsDelimeter Delimeter of the thousands place. + * @param {string} decimalDelimeter Delimeter of the decimal point. + * @param {print_preview.MeasurementSystem.UnitType} unitType Type of unit + * of the local measurement system. + */ + initialize: function( + isDocumentModifiable, + isDuplexEnabled, + isHeaderFooterEnabled, + marginsType, + customMargins, + thousandsDelimeter, + decimalDelimeter, + unitType) { + + this.documentInfo_.isModifiable = isDocumentModifiable; + this.measurementSystem_.setSystem( + thousandsDelimeter, decimalDelimeter, unitType); + + // Initialize ticket with user's previous values. + this.duplex_.updateValue(isDuplexEnabled); + this.headerFooter_.updateValue(isHeaderFooterEnabled); + if (marginsType != null) { + this.marginsType_.updateValue(marginsType); + } + if (customMargins != null) { + this.customMargins_.updateValue(customMargins); + } + }, + + /** + * Updates the capabilities of the destination the print ticket is for. + * Dispatches a CAPABILITIES_CHANGE event. + * @param {!print_preview.ChromiumCapabilities} caps New capabilities. + */ + updateDestinationCapabilities: function(caps) { + var isFirstUpdate = this.capabilitiesHolder_.get() == null; + this.capabilitiesHolder_.set(caps); + if (isFirstUpdate) { + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE); + } else { + this.customMargins_.updateValue(null); + if (this.marginsType_.getValue() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + this.marginsType_.updateValue( + print_preview.ticket_items.MarginsType.Value.DEFAULT); + } + cr.dispatchSimpleEvent( + this, PrintTicketStore.EventType.CAPABILITIES_CHANGE); + } + }, + + /** @return {boolean} Whether the ticket store has the copies capability. */ + hasCopiesCapability: function() { + return this.copies_.isCapabilityAvailable(); + }, + + /** + * @return {boolean} Whether the string representation of the copies value + * currently in the ticket store is valid. + */ + isCopiesValid: function() { + return this.copies_.isValid(); + }, + + isCopiesValidForValue: function(value) { + return this.copies_.wouldValueBeValid(value); + }, + + /** @return {number} Number of copies to print. */ + getCopies: function() { + return this.copies_.getValueAsNumber(); + }, + + /** + * @return {string} String representation of the number of copies to print. + */ + getCopiesStr: function() { + return this.copies_.getValue(); + }, + + /** + * Updates the string representation of the number of copies to print. + * Dispatches a TICKET_CHANGE event if the string value has changed. + * @param {string} New string representation of the number of copies to + * print. + */ + updateCopies: function(copies) { + if (this.copies_.getValue() != copies) { + this.copies_.updateValue(copies); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the ticket store has a collate capability. */ + hasCollateCapability: function() { + return this.collate_.isCapabilityAvailable(); + }, + + /** @return {boolean} Whether collate is enabled. */ + isCollateEnabled: function() { + return this.collate_.getValue(); + }, + + /** + * Updates whether collate is enabled. Dispatches a TICKET_CHANGE event if + * collate has changed. + * @param {boolean} isCollateEnabled Whether collate is enabled. + */ + updateCollate: function(isCollateEnabled) { + if (this.collate_.getValue() != isCollateEnabled) { + this.collate_.updateValue(isCollateEnabled); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** + * @return {boolean} Whether the ticket store has color printing capability. + */ + hasColorCapability: function() { + return this.color_.isCapabilityAvailable(); + }, + + /** @return {boolean} Whether color printing is enabled. */ + isColorEnabled: function() { + return this.color_.getValue(); + }, + + /** + * Updates whether color printing is enabled. Dispatches a TICKET_CHANGE if + * color has changed. + * @param {boolean} isColorEnabled Whether the color printing is enabled. + */ + updateColor: function(isColorEnabled) { + if (this.color_.getValue() != isColorEnabled) { + this.color_.updateValue(isColorEnabled); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the header-footer capability is available. */ + hasHeaderFooterCapability: function() { + return this.headerFooter_.isCapabilityAvailable(); + }, + + /** @return {boolean} Whether the header-footer setting is enabled. */ + isHeaderFooterEnabled: function() { + return this.headerFooter_.getValue(); + }, + + /** + * Updates the whether the header-footer setting is enabled. Dispatches a + * TICKET_CHANGE event if the setting changed. + * @param {boolean} isHeaderFooterEnabled Whether the header-footer setting + * is enabled. + */ + updateHeaderFooter: function(isHeaderFooterEnabled) { + if (this.headerFooter_.getValue() != isHeaderFooterEnabled) { + this.headerFooter_.updateValue(isHeaderFooterEnabled); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** + * @return {boolean} Whether the page orientation capability is available. + */ + hasOrientationCapability: function() { + return this.landscape_.isCapabilityAvailable(); + }, + + /** + * @return {boolean} Whether the document should be printed in landscape. + */ + isLandscapeEnabled: function() { + return this.landscape_.getValue(); + }, + + /** + * Updates whether the document should be printed in landscape. Dispatches + * a TICKET_CHANGE event if the setting changes. + * @param {boolean} isLandscapeEnabled Whether the document should be + * printed in landscape. + */ + updateOrientation: function(isLandscapeEnabled) { + if (this.landscape_.getValue() != isLandscapeEnabled) { + this.landscape_.updateValue(isLandscapeEnabled); + // Reset the user set margins. + this.marginsType_.updateValue( + print_preview.ticket_items.MarginsType.Value.DEFAULT); + this.customMargins_.updateValue(null); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the duplexing capability is available. */ + hasDuplexCapability: function() { + return this.duplex_.isCapabilityAvailable(); + }, + + /** @return {boolean} Whether the document should be printed in duplex. */ + isDuplexEnabled: function() { + return this.duplex_.getValue(); + }, + + /** + * Updates the duplexing setting. Dispatches a TICKET_CHANGE event if the + * value changes. + * @param {boolean} isDuplexEnabled Whether the document should be printed + * in duplex. + */ + updateDuplex: function(isDuplexEnabled) { + if (this.duplex_.getValue() != isDuplexEnabled) { + this.duplex_.updateValue(isDuplexEnabled); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the margins capability is available. */ + hasMarginsCapability: function() { + return this.marginsType_.isCapabilityAvailable(); + }, + + /** + * @return {print_preview.ticket_items.MarginsType.Value} Type of predefined + * margins. + */ + getMarginsType: function() { + return this.marginsType_.getValue(); + }, + + /** + * Updates the type of predefined margins. Dispatches a TICKET_CHANGE event + * if the margins type changes. + * @param {print_preview.ticket_items.MarginsType.Value} marginsType Type of + * predefined margins. + */ + updateMarginsType: function(marginsType) { + if (this.marginsType_.getValue() != marginsType) { + this.marginsType_.updateValue(marginsType); + if (marginsType == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + // If CUSTOM, set the value of the custom margins so that it won't be + // overridden by the default value. + this.customMargins_.updateValue(this.customMargins_.getValue()); + } + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether all of the custom margins are valid. */ + isCustomMarginsValid: function() { + return this.customMargins_.isValid(); + }, + + /** + * @return {!print_preview.Margins} Custom margins of the document in + * points. + */ + getCustomMargins: function() { + return this.customMargins_.getValue(); + }, + + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin to get the maximum value for. + * @return {number} Maximum value in points of the specified margin. + */ + getCustomMarginMax: function(orientation) { + return this.customMargins_.getMarginMax(orientation); + }, + + /** + * Updates the custom margins of the document. Dispatches a TICKET_CHANGE + * event if the margins have changed. + * @param {!print_preview.Margins} margins New document page margins in + * points. + */ + updateCustomMargins: function(margins) { + if (!this.isCustomMarginsValid() || + !margins.equals(this.getCustomMargins())) { + this.customMargins_.updateValue(margins); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** + * Updates a single custom margin's value in points. + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin to update. + * @param {number} value Updated margin in points. + */ + updateCustomMargin: function(orientation, value) { + if (this.customMargins_.getValue().get(orientation) != value) { + this.customMargins_.updateMargin(orientation, value); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the page range capability is available. */ + hasPageRangeCapability: function() { + return this.pageRange_.isCapabilityAvailable(); + }, + + /** + * @return {boolean} Whether the current page range string is defines a + * valid page number set. + */ + isPageRangeValid: function() { + return this.pageRange_.isValid(); + }, + + /** @return {string} String representation of the page range. */ + getPageRangeStr: function() { + return this.pageRange_.getValue(); + }, + + /** + * @return {!print_preview.PageNumberSet} Page number set specified by the + * string representation of the page range string. + */ + getPageNumberSet: function() { + return this.pageRange_.getPageNumberSet(); + }, + + /** + * Updates the page range string. Dispatches a TICKET_CHANGE if the string + * changed. + * @param {string} pageRangeStr New page range string. + */ + updatePageRange: function(pageRangeStr) { + if (this.pageRange_.getValue() != pageRangeStr) { + this.pageRange_.updateValue(pageRangeStr); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** @return {boolean} Whether the fit-to-page capability is available. */ + hasFitToPageCapability: function() { + return this.fitToPage_.isCapabilityAvailable(); + }, + + /** @return {boolean} Whether the fit-to-page capability is enabled. */ + isFitToPageEnabled: function() { + return this.fitToPage_.getValue(); + }, + + /** + * @param {boolean} isFitToPageEnabled Whether to enable the fit-to-page + * capability. + */ + updateFitToPage: function(isFitToPageEnabled) { + if (this.fitToPage_.getValue() != isFitToPageEnabled) { + this.fitToPage_.updateValue(isFitToPageEnabled); + cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.TICKET_CHANGE); + } + }, + + /** + * @return {boolean} {@code true} if the stored print ticket is valid, + * {@code false} otherwise. + */ + isTicketValid: function() { + return this.isTicketValidForPreview() && + (!this.hasPageRangeCapability() || this.isPageRangeValid()); + }, + + /** @return {boolean} Whether the ticket is valid for preview generation. */ + isTicketValidForPreview: function() { + return (!this.hasCopiesCapability() || this.isCopiesValid()) && + (!this.hasMarginsCapability() || + this.getMarginsType() != + print_preview.ticket_items.MarginsType.Value.CUSTOM || + this.isCustomMarginsValid()); + } + }; + + // Export + return { + PrintTicketStore: PrintTicketStore + }; +}); diff --git a/chrome/browser/resources/print_preview/data/printable_area.js b/chrome/browser/resources/print_preview/data/printable_area.js new file mode 100644 index 0000000..4dd4955 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/printable_area.js @@ -0,0 +1,64 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Object describing the printable area of a page in the document. + * @param {!print_preview.Coordinate2d} origin Top left corner of the + * printable area of the document. + * @param {!print_preview.Size} size Size of the printable area of the + * document. + * @constructor + */ + function PrintableArea(origin, size) { + /** + * Top left corner of the printable area of the document. + * @type {!print_preview.Coordinate2d} + * @private + */ + this.origin_ = origin; + + /** + * Size of the printable area of the document. + * @type {!print_preview.Size} + * @private + */ + this.size_ = size; + }; + + PrintableArea.prototype = { + /** + * @return {!print_preview.Coordinate2d} Top left corner of the printable + * area of the document. + */ + get origin() { + return this.origin_; + }, + + /** + * @return {!print_preview.Size} Size of the printable area of the document. + */ + get size() { + return this.size_; + }, + + /** + * @param {print_preview.PrintableArea} other Other printable area to check + * for equality. + * @return {boolean} Whether another printable area is equal to this one. + */ + equals: function(other) { + return other != null && + this.origin_.equals(other.origin_) && + this.size_.equals(other.size_); + } + }; + + // Export + return { + PrintableArea: PrintableArea + }; +}); diff --git a/chrome/browser/resources/print_preview/data/size.js b/chrome/browser/resources/print_preview/data/size.js new file mode 100644 index 0000000..7b43418 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/size.js @@ -0,0 +1,56 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Immutable two-dimensional size. + * @param {number} width Width of the size. + * @param {number} height Height of the size. + * @constructor + */ + function Size(width, height) { + /** + * Width of the size. + * @type {number} + * @private + */ + this.width_ = width; + + /** + * Height of the size. + * @type {number} + * @private + */ + this.height_ = height; + }; + + Size.prototype = { + /** @return {number} Width of the size. */ + get width() { + return this.width_; + }, + + /** @return {number} Height of the size. */ + get height() { + return this.height_; + }, + + /** + * @param {print_preview.Size} other Other size object to compare against. + * @return {boolean} Whether this size object is equal to another. + */ + equals: function(other) { + return other != null && + this.width_ == other.width_ && + this.height_ == other.height_; + } + }; + + // Export + return { + Size: Size + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/collate.js b/chrome/browser/resources/print_preview/data/ticket_items/collate.js new file mode 100644 index 0000000..10ddcf1 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/collate.js @@ -0,0 +1,57 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Collate ticket item whose value is a {@code boolean} that indicates whether + * collation is enabled. + * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities + * holder used to determine the default collate value and if the collate + * capability is available. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function Collate(capabilitiesHolder) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Capabilities holder used to determine the default collate value and if + * the collate capability is available. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = capabilitiesHolder; + }; + + Collate.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.capabilitiesHolder_.get().hasCollateCapability; + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.capabilitiesHolder_.get().defaultIsCollateEnabled; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return false; + } + }; + + // Export + return { + Collate: Collate + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/color.js b/chrome/browser/resources/print_preview/data/ticket_items/color.js new file mode 100644 index 0000000..b03d407 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/color.js @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Color ticket item whose value is a {@code boolean} that indicates whether + * the document should be printed in color. + * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities + * holder used to determine the default color value and if the color + * capability is available. + * @param {!print_preview.DestinationStore} destinationStore Used to determine + * whether color printing should be available. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function Color(capabilitiesHolder, destinationStore) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Capabilities holder used to determine the default color value and if the + * color capability is available. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = capabilitiesHolder; + + /** + * Used to determine whether color printing should be available. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + }; + + Color.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.capabilitiesHolder_.get().hasColorCapability && + (!this.destinationStore_.selectedDestination || + this.destinationStore_.selectedDestination.id != + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF); + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.capabilitiesHolder_.get().defaultIsColorEnabled; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return this.destinationStore_.selectedDestination && + this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; + } + }; + + // Export + return { + Color: Color + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/copies.js b/chrome/browser/resources/print_preview/data/ticket_items/copies.js new file mode 100644 index 0000000..7cd1620 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/copies.js @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Copies ticket item whose value is a {@code string} that indicates how many + * copies of the document should be printed. The ticket item is backed by a + * string since the user can textually input the copies value. + * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities + * holder used to determine the default number of copies and if the copies + * capability is available. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function Copies(capabilitiesHolder) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Capabilities holder used to determine the default number of copies and if + * the copies capability is available. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = capabilitiesHolder; + }; + + Copies.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + if (/[^\d]+/.test(value)) { + return false; + } + var copies = parseInt(value); + if (copies > 999 || copies < 1) { + return false; + } + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.capabilitiesHolder_.get().hasCopiesCapability; + }, + + /** @return {number} The number of copies indicated by the ticket item. */ + getValueAsNumber: function() { + return parseInt(this.getValue()); + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.capabilitiesHolder_.get().defaultCopiesStr; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return '1'; + } + }; + + // Export + return { + Copies: Copies + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js b/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js new file mode 100644 index 0000000..774e35b --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/custom_margins.js @@ -0,0 +1,171 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Custom page margins ticket item whose value is a + * {@code print_preview.Margins}. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.MeasurementSystem} measurementSystem Used to convert + * from string input into measurements in points. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function CustomMargins(documentInfo, measurementSystem) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + + /** + * Used to convert from string input to measurements in points. + * @type {!print_preview.MeasurementSystem} + * @private + */ + this.measurementSystem_ = measurementSystem; + }; + + /** + * Enumeration of the orientations of margins. + * @enum {string} + */ + CustomMargins.Orientation = { + TOP: 'top', + RIGHT: 'right', + BOTTOM: 'bottom', + LEFT: 'left' + }; + + /** + * Mapping of a margin orientation to its opposite. + * @type {object.<CustomMargins.Orientation, CustomMargins.Orientation>} + * @private + */ + CustomMargins.OppositeOrientation_ = {}; + CustomMargins.OppositeOrientation_[CustomMargins.Orientation.TOP] = + CustomMargins.Orientation.BOTTOM; + CustomMargins.OppositeOrientation_[CustomMargins.Orientation.RIGHT] = + CustomMargins.Orientation.LEFT; + CustomMargins.OppositeOrientation_[CustomMargins.Orientation.BOTTOM] = + CustomMargins.Orientation.TOP; + CustomMargins.OppositeOrientation_[CustomMargins.Orientation.LEFT] = + CustomMargins.Orientation.RIGHT; + + /** + * Minimum distance in points that two margins can be separated by. + * @type {number} + * @const + * @private + */ + CustomMargins.MINIMUM_MARGINS_DISTANCE_ = 72; // 1 inch. + + CustomMargins.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + var margins = /** @type {!print_preview.Margins} */ (value); + for (var key in CustomMargins.Orientation) { + var o = CustomMargins.Orientation[key]; + var max = this.getMarginMax_( + o, margins.get(CustomMargins.OppositeOrientation_[o])); + if (margins.get(o) > max || margins.get(o) < 0) { + return false; + } + } + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.documentInfo_.isModifiable; + }, + + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin to get the maximum value for. + * @return {number} Maximum value in points of the specified margin. + */ + getMarginMax: function(orientation) { + var oppositeOrient = CustomMargins.OppositeOrientation_[orientation]; + var margins = /** @type {!print_preview.Margins} */ (this.getValue()); + return this.getMarginMax_(orientation, margins.get(oppositeOrient)); + }, + + /** @override */ + updateValue: function(value) { + var margins = /** @type {!InputMargins} */ (value); + if (margins != null) { + margins = new print_preview.Margins( + Math.round(margins.get(CustomMargins.Orientation.TOP)), + Math.round(margins.get(CustomMargins.Orientation.RIGHT)), + Math.round(margins.get(CustomMargins.Orientation.BOTTOM)), + Math.round(margins.get(CustomMargins.Orientation.LEFT))); + } + print_preview.ticket_items.TicketItem.prototype.updateValue.call( + this, margins); + }, + + /** + * Updates the specified margin in points while keeping the value within + * a maximum and minimum. + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies the margin to update. + * @param {number} value Updated margin value in points. + */ + updateMargin: function(orientation, value) { + var margins = /** @type {!print_preview.Margins} */ (this.getValue()); + var oppositeOrientation = CustomMargins.OppositeOrientation_[orientation]; + var max = + this.getMarginMax_(orientation, margins.get(oppositeOrientation)); + value = Math.max(0, Math.min(max, value)); + this.updateValue(margins.set(orientation, value)); + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.documentInfo_.margins || + new print_preview.Margins(72, 72, 72, 72); + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return this.documentInfo_.margins || + new print_preview.Margins(72, 72, 72, 72); + }, + + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Specifies which margin to get the maximum value of. + * @param {number} oppositeMargin Value of the margin in points + * opposite the specified margin. + * @return {number} Maximum value in points of the specified margin. + * @private + */ + getMarginMax_: function(orientation, oppositeMargin) { + var max; + if (orientation == CustomMargins.Orientation.TOP || + orientation == CustomMargins.Orientation.BOTTOM) { + max = this.documentInfo_.pageSize.height - oppositeMargin - + CustomMargins.MINIMUM_MARGINS_DISTANCE_; + } else { + max = this.documentInfo_.pageSize.width - oppositeMargin - + CustomMargins.MINIMUM_MARGINS_DISTANCE_; + } + return Math.round(max); + } + }; + + // Export + return { + CustomMargins: CustomMargins + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/duplex.js b/chrome/browser/resources/print_preview/data/ticket_items/duplex.js new file mode 100644 index 0000000..c482982 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/duplex.js @@ -0,0 +1,57 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Duplex ticket item whose value is a {@code boolean} that indicates whether + * the document should be duplex printed. + * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities + * holder used to determine the default duplex value and if duplexing + * is available. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function Duplex(capabilitiesHolder) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Capabilities holder used to determine the default duplex value and if + * duplexing is available. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = capabilitiesHolder; + }; + + Duplex.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.capabilitiesHolder_.get().hasDuplexCapability; + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.capabilitiesHolder_.get().defaultIsDuplexEnabled; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return false; + } + }; + + // Export + return { + Duplex: Duplex + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js b/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js new file mode 100644 index 0000000..40d4a75 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/fit_to_page.js @@ -0,0 +1,69 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Fit-to-page ticket item whose value is a {@code boolean} that indicates + * whether to scale the document to fit the page. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.DestinationStore} destinationStore Used to determine + * whether fit to page should be available. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function FitToPage(documentInfo, destinationStore) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + + /** + * Used to determine whether fit to page should be available. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + }; + + FitToPage.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return !this.documentInfo_.isModifiable && + (!this.destinationStore_.selectedDestination || + this.destinationStore_.selectedDestination.id != + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF); + }, + + /** @override */ + getDefaultValueInternal: function() { + return true; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return this.destinationStore_.selectedDestination && + this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; + } + }; + + // Export + return { + FitToPage: FitToPage + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js b/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js new file mode 100644 index 0000000..db8e6ff --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/header_footer.js @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Header-footer ticket item whose value is a {@code boolean} that indicates + * whether the document should be printed with headers and footers. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @param {!print_preview.ticket_items.MarginsType} marginsType Ticket item + * that stores which predefined margins to print with. + * @param {!print_preview.ticket_items.CustomMargins} customMargins Ticket + * item that stores custom margin values. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function HeaderFooter(documentInfo, marginsType, customMargins) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + + /** + * Ticket item that stores which predefined margins to print with. + * @type {!print_preview.ticket_items.MarginsType} + * @private + */ + this.marginsType_ = marginsType; + + /** + * Ticket item that stores custom margin values. + * @type {!print_preview.ticket_items.CustomMargins} + * @private + */ + this.customMargins_ = customMargins; + }; + + HeaderFooter.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + if (!this.documentInfo_.isModifiable) { + return false; + } else if (this.marginsType_.getValue() == + print_preview.ticket_items.MarginsType.Value.NO_MARGINS) { + return false; + } else if (this.marginsType_.getValue() == + print_preview.ticket_items.MarginsType.Value.MINIMUM) { + return true; + } + var margins; + if (this.marginsType_.getValue() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + if (!this.customMargins_.isValid()) { + return false; + } + margins = this.customMargins_.getValue(); + } else { + margins = this.documentInfo_.margins; + } + var orientEnum = print_preview.ticket_items.CustomMargins.Orientation; + return margins == null || + margins.get(orientEnum.TOP) > 0 || + margins.get(orientEnum.BOTTOM) > 0; + }, + + /** @override */ + getDefaultValueInternal: function() { + return true; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return false; + } + }; + + // Export + return { + HeaderFooter: HeaderFooter + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/landscape.js b/chrome/browser/resources/print_preview/data/ticket_items/landscape.js new file mode 100644 index 0000000..eac17bb --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/landscape.js @@ -0,0 +1,72 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Landscape ticket item whose value is a {@code boolean} that indicates + * whether the document should be printed in landscape orientation. + * @param {!print_preview.CapabilitiesHolder} capabilitiesHolder Capabilities + * holder used to determine the default landscape value and if landscape + * printing is available. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function Landscape(capabilitiesHolder, documentInfo) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Capabilities holder used to determine the default landscape value and if + * landscape printing is available. + * @type {!print_preview.CapabilitiesHolder} + * @private + */ + this.capabilitiesHolder_ = capabilitiesHolder; + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + }; + + Landscape.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + // TODO(rltoscano): Technically, the print destination can still change + // the orientation of the print out (at least for cloud printers) if the + // document is not modifiable. But the preview wouldn't update in this + // case so it would be a bad user experience. + return this.documentInfo_.isModifiable && + !this.documentInfo_.hasCssMediaStyles && + this.capabilitiesHolder_.get().hasOrientationCapability; + }, + + /** @override */ + getDefaultValueInternal: function() { + return this.capabilitiesHolder_.get().defaultIsLandscapeEnabled; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return false; + } + }; + + // Export + return { + Landscape: Landscape + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js b/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js new file mode 100644 index 0000000..3fe95f4 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/margins_type.js @@ -0,0 +1,68 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Margins type ticket item whose value is a + * {@link print_preview.ticket_items.MarginsType.Value} that indicates what + * predefined margins type to use. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function MarginsType(documentInfo) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + }; + + /** + * Enumeration of margin types. Matches enum MarginType in + * printing/print_job_constants.h. + * @enum {number} + */ + MarginsType.Value = { + DEFAULT: 0, + NO_MARGINS: 1, + MINIMUM: 2, + CUSTOM: 3 + }; + + MarginsType.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return true; + }, + + /** @override */ + isCapabilityAvailable: function() { + return this.documentInfo_.isModifiable; + }, + + /** @override */ + getDefaultValueInternal: function() { + return MarginsType.Value.DEFAULT; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return MarginsType.Value.DEFAULT; + } + }; + + // Export + return { + MarginsType: MarginsType + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/page_range.js b/chrome/browser/resources/print_preview/data/ticket_items/page_range.js new file mode 100644 index 0000000..2fc7152 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/page_range.js @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * Page range ticket item whose value is a {@code string} that represents + * which pages in the document should be printed. + * @param {!print_preview.DocumentInfo} documentInfo Information about the + * document to print. + * @constructor + * @extends {print_preview.ticket_items.TicketItem} + */ + function PageRange(documentInfo) { + print_preview.ticket_items.TicketItem.call(this); + + /** + * Information about the document to print. + * @type {!print_preview.DocumentInfo} + * @private + */ + this.documentInfo_ = documentInfo; + }; + + PageRange.prototype = { + __proto__: print_preview.ticket_items.TicketItem.prototype, + + /** @override */ + wouldValueBeValid: function(value) { + return value == '' || + isPageRangeTextValid(value, this.documentInfo_.pageCount); + }, + + /** + * @return {!print_preview.PageNumberSet} Set of page numbers defined by the + * page range string. + */ + getPageNumberSet: function() { + if (this.isValid()) { + return print_preview.PageNumberSet.parse( + this.getValue(), this.documentInfo_.pageCount); + } else { + return print_preview.PageNumberSet.parse( + this.getDefaultValueInternal(), this.documentInfo_.pageCount); + } + }, + + /** @override */ + isCapabilityAvailable: function() { + return true; + }, + + /** @override */ + getDefaultValueInternal: function() { + return ''; + }, + + /** @override */ + getCapabilityNotAvailableValueInternal: function() { + return ''; + } + }; + + // Export + return { + PageRange: PageRange + }; +}); diff --git a/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js b/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js new file mode 100644 index 0000000..1dffd20 --- /dev/null +++ b/chrome/browser/resources/print_preview/data/ticket_items/ticket_item.js @@ -0,0 +1,95 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview.ticket_items', function() { + 'use strict'; + + /** + * An object that represents a user modifiable item in a print ticket. Each + * ticket item has a value which can be set by the user. Ticket items can also + * be unavailable for modifying if the print destination doesn't support it or + * if other ticket item constraints are not met. + * @constructor + */ + function TicketItem() { + /** + * Backing store of the print ticket item. + * @type {Object} + * @private + */ + this.value_ = null; + }; + + TicketItem.prototype = { + /** + * Determines whether a given value is valid for the ticket item. + * @param {Object} value The value to check for validity. + * @return {boolean} Whether the given value is valid for the ticket item. + */ + wouldValueBeValid: function(value) { + throw Error('Abstract method not overridden'); + }, + + /** + * @return {boolean} Whether the print destination capability is available. + */ + isCapabilityAvailable: function() { + throw Error('Abstract method not overridden'); + }, + + /** @return {object} The value of the ticket item. */ + getValue: function() { + if (this.isCapabilityAvailable()) { + if (this.value_ == null) { + return this.getDefaultValueInternal(); + } else { + return this.value_; + } + } else { + return this.getCapabilityNotAvailableValueInternal(); + } + }, + + /** @return {boolean} Whether the ticket item was modified by the user. */ + isUserEdited: function() { + return this.value_ != null; + }, + + /** @return {boolean} Whether the ticket item's value is valid. */ + isValid: function() { + if (!this.isUserEdited()) { + return true; + } + return this.wouldValueBeValid(this.value_); + }, + + /** @param {object} Value to set as the value of the ticket item. */ + updateValue: function(value) { + this.value_ = value; + }, + + /** + * @return {object} Default value of the ticket item if no value was set by + * the user. + * @protected + */ + getDefaultValueInternal: function() { + throw Error('Abstract method not overridden'); + }, + + /** + * @return {object} Default value of the ticket item if the capability is + * not available. + * @protected + */ + getCapabilityNotAvailableValueInternal: function() { + throw Error('Abstract method not overridden'); + } + }; + + // Export + return { + TicketItem: TicketItem + }; +}); diff --git a/chrome/browser/resources/print_preview/fit_to_page_settings.js b/chrome/browser/resources/print_preview/fit_to_page_settings.js deleted file mode 100644 index 6747127..0000000 --- a/chrome/browser/resources/print_preview/fit_to_page_settings.js +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a |FitToPageSettings| object. This object encapsulates all - * settings and logic related to the fit to page checkbox. - * @constructor - */ - function FitToPageSettings() { - // @type {HTMLDivElement} This represents fit to page div element. - this.fitToPageOption_ = $('fit-to-page-option'); - - // @type {HTMLInputElement} This represents fit to page input element. - this.fitToPageCheckbox_ = $('fit-to-page'); - - // @type {boolean} True if fit to page option applies for the selected - // user options. Fit to Page options applies only if we are previewing - // a PDF and the current destination printer is actually a physcial - // printer. - this.fitToPageApplies_ = true; - - this.addEventListeners_(); - } - - cr.addSingletonGetter(FitToPageSettings); - - FitToPageSettings.prototype = { - /** - * Returns true if we need to fit the page contents to printable area. - * @return {boolean} true if Fit to page is checked. - */ - hasFitToPage: function() { - return previewModifiable || this.fitToPageCheckbox_.checked; - }, - - /** - * Updates |this.fitToPageApplies_| depending on the selected printer and - * preview data source type. - * @param {!string} printerName Selected printer name. - * @private - */ - resetState_: function(printerName) { - if (!previewModifiable) - isPrintReadyMetafileReady = false; - var printToPDF = printerName == PRINT_TO_PDF; - this.fitToPageApplies_ = !previewModifiable && !printToPDF; - }, - - /** - * Print scaling is disabled for preview source plugin. Uncheck the fit to - * page option. - */ - onPrintScalingDisabled: function() { - this.fitToPageCheckbox_.checked = false; - }, - - /** - * Adding listeners to fit to page control. - * @private - */ - addEventListeners_: function() { - this.fitToPageCheckbox_.onclick = - this.onFitToPageCheckboxClicked_.bind(this); - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - document.addEventListener(customEvents.PRINTER_SELECTION_CHANGED, - this.onPrinterSelectionChanged_.bind(this)); - }, - - /** - * Listener executing when a |customEvents.PRINTER_SELECTION_CHANGED| event - * occurs. - * @param {cr.Event} event The event that triggered this listener. - * @private - */ - onPrinterSelectionChanged_: function(event) { - this.resetState_(event.selectedPrinter); - this.updateVisibility_(); - }, - - /** - * Listener executing when the user selects or de-selects the fit to page - * option. - * @private - */ - onFitToPageCheckboxClicked_: function() { - requestPrintPreview(); - }, - - /** - * Listener executing when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - this.updateVisibility_(); - }, - - /** - * Hides or shows |this.fitToPageOption_|. - * @private - */ - updateVisibility_: function() { - this.fitToPageOption_.style.display = - this.fitToPageApplies_ ? 'block' : 'none'; - } - }; - - return { - FitToPageSettings: FitToPageSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/header_footer_settings.js b/chrome/browser/resources/print_preview/header_footer_settings.js deleted file mode 100644 index df74c67..0000000 --- a/chrome/browser/resources/print_preview/header_footer_settings.js +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a HeaderFooterSettings object. This object encapsulates all - * settings and logic related to the headers and footers checkbox. - * @constructor - */ - function HeaderFooterSettings() { - this.headerFooterOption_ = $('header-footer-option'); - this.headerFooterCheckbox_ = $('header-footer'); - this.addEventListeners_(); - } - - cr.addSingletonGetter(HeaderFooterSettings); - - HeaderFooterSettings.prototype = { - /** - * The checkbox corresponding to the headers and footers option. - * @type {HTMLInputElement} - */ - get headerFooterCheckbox() { - return this.headerFooterCheckbox_; - }, - - /** - * Checks whether the Headers and Footers checkbox is checked or not. - * @return {boolean} true if Headers and Footers are checked. - */ - hasHeaderFooter: function() { - return previewModifiable && this.headerFooterCheckbox_.checked; - }, - - /** - * Sets the state of the headers footers checkbox. - * @param {boolean} checked True if the headers footers checkbox shoule be - * checked, false if not. - */ - setChecked: function(checked) { - this.headerFooterCheckbox_.checked = checked; - }, - - /** - * Checks the printable area and updates the visibility of header footer - * option based on the selected margins. - * @param {{contentWidth: number, contentHeight: number, marginLeft: number, - * marginRight: number, marginTop: number, marginBottom: number, - * printableAreaX: number, printableAreaY: number, - * printableAreaWidth: number, printableAreaHeight: number}} - * pageLayout Specifies default page layout details in points. - * @param {number} marginsType Specifies the selected margins type value. - */ - checkAndHideHeaderFooterOption: function(pageLayout, marginsType) { - var headerFooterApplies = true; - if (marginsType == - print_preview.MarginSettings.MARGINS_VALUE_NO_MARGINS || - !previewModifiable) { - headerFooterApplies = false; - } else if (marginsType != - print_preview.MarginSettings.MARGINS_VALUE_MINIMUM) { - if (cr.isLinux || cr.isChromeOS) { - headerFooterApplies = pageLayout.marginTop > 0 || - pageLayout.marginBottom > 0; - } else { - var pageHeight = pageLayout.marginTop + pageLayout.marginBottom + - pageLayout.contentHeight; - headerFooterApplies = - (pageLayout.marginTop > pageLayout.printableAreaY) || - (pageLayout.marginBottom > - (pageHeight - pageLayout.printableAreaY - - pageLayout.printableAreaHeight)); - } - } - this.setVisible_(headerFooterApplies); - var headerFooterEvent = new cr.Event( - customEvents.HEADER_FOOTER_VISIBILITY_CHANGED); - headerFooterEvent.headerFooterApplies = headerFooterApplies; - document.dispatchEvent(headerFooterEvent); - }, - - /** - * Adding listeners to header footer related controls. - * @private - */ - addEventListeners_: function() { - this.headerFooterCheckbox_.onclick = - this.onHeaderFooterChanged_.bind(this); - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - }, - - /** - * Listener executing when the user selects or de-selects the headers - * and footers option. - * @private - */ - onHeaderFooterChanged_: function() { - requestPrintPreview(); - }, - - /** - * Listener executing when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - if (!previewModifiable) - this.setVisible_(false); - }, - - /** - * Hides or shows |this.headerFooterOption_|. - * @param {boolean} visible True if |this.headerFooterOption_| should be - * shown. - * @private - */ - setVisible_: function(visible) { - this.headerFooterOption_.style.display = visible ? 'block' : 'none'; - }, - }; - - return { - HeaderFooterSettings: HeaderFooterSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/layout_settings.html b/chrome/browser/resources/print_preview/layout_settings.html deleted file mode 100644 index 8bdf80f..0000000 --- a/chrome/browser/resources/print_preview/layout_settings.html +++ /dev/null @@ -1,13 +0,0 @@ -<div id="layout-option" class="two-column visible"> - <h1 i18n-content="layoutLabel"></h1> - <div class="right-column"> - <div class="radio"><label> - <input id="portrait" type="radio" name="layout" checked> - <span i18n-content="optionPortrait"></span> - </label></div> - <div class="radio"><label> - <input id="landscape" type="radio" name="layout"> - <span i18n-content="optionLandscape"></span> - </label></div> - </div> -</div> diff --git a/chrome/browser/resources/print_preview/layout_settings.js b/chrome/browser/resources/print_preview/layout_settings.js deleted file mode 100644 index e6cc097..0000000 --- a/chrome/browser/resources/print_preview/layout_settings.js +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a LayoutSettings object. This object encapsulates all settings and - * logic related to layout mode (portrait/landscape). - * @constructor - */ - function LayoutSettings() { - this.layoutOption_ = $('layout-option'); - this.portraitRadioButton_ = $('portrait'); - this.landscapeRadioButton_ = $('landscape'); - this.wasLandscape_ = false; - this.updateState(); - this.addEventListeners_(); - } - - cr.addSingletonGetter(LayoutSettings); - - LayoutSettings.prototype = { - /** - * The radio button corresponding to the portrait option. - * @type {HTMLInputElement} - */ - get portraitRadioButton() { - return this.portraitRadioButton_; - }, - - /** - * The radio button corresponding to the landscape option. - * @type {HTMLInputElement} - */ - get landscapeRadioButton() { - return this.landscapeRadioButton_; - }, - - /** - * @return {boolean} true if |this.landscapeRadioButton_| is checked, false - * if not. - */ - isLandscape: function() { - return this.landscapeRadioButton_.checked; - }, - - /** - * @return {boolean} true if the chosen layout mode has changed since last - * time the state was updated. - */ - hasChanged_: function() { - return this.isLandscape() != this.wasLandscape_; - }, - - /** - * Saves the currently selected layout mode. Used in |this.hasChanged_|. - */ - updateState: function() { - this.wasLandscape_ = this.isLandscape(); - }, - - /** - * Adding listeners to all layout related controls. The listeners take care - * of altering their behavior depending on |hasPendingPreviewRequest|. - * @private - */ - addEventListeners_: function() { - this.landscapeRadioButton_.onclick = this.onLayoutButtonClick_.bind(this); - this.portraitRadioButton_.onclick = this.onLayoutButtonClick_.bind(this); - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - document.addEventListener(customEvents.PRINTER_CAPABILITIES_UPDATED, - this.onPrinterCapabilitiesUpdated_.bind(this)); - }, - - /** - * Executes when a |customEvents.PRINTER_CAPABILITIES_UPDATED| event occurs. - * @private - */ - onPrinterCapabilitiesUpdated_: function(e) { - if (e.printerCapabilities.disableLandscapeOption) - this.fadeInOut_(e.printerCapabilities.disableLandscapeOption); - }, - - /** - * Listener executing when |this.landscapeRadioButton_| or - * |this.portraitRadioButton_| is clicked. - * @private - */ - onLayoutButtonClick_: function() { - // If the chosen layout is same as before, nothing needs to be done. - if (this.hasChanged_()) - setDefaultValuesAndRegeneratePreview(true); - }, - - /** - * Listener executing when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - this.fadeInOut_(!previewModifiable || hasPageSizeStyle); - }, - - /** - * @param {boolean} fadeOut True if |this.layoutOption_| should be faded - * out, false if it should be faded in. - * @private - */ - fadeInOut_: function(fadeOut) { - fadeOut ? fadeOutOption(this.layoutOption_) : - fadeInOption(this.layoutOption_); - } - }; - - return { - LayoutSettings: LayoutSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/margin_settings.js b/chrome/browser/resources/print_preview/margin_settings.js deleted file mode 100644 index 0ffca9f..0000000 --- a/chrome/browser/resources/print_preview/margin_settings.js +++ /dev/null @@ -1,705 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a Margins object that holds four margin values. The units in which - * the values are expressed can be any numeric value. - * @constructor - * @param {number} left The left margin. - * @param {number} top The top margin. - * @param {number} right The right margin. - * @param {number} bottom The bottom margin. - */ - function Margins(left, top, right, bottom) { - this[MarginSettings.LEFT_GROUP] = left; - this[MarginSettings.TOP_GROUP] = top; - this[MarginSettings.RIGHT_GROUP] = right; - this[MarginSettings.BOTTOM_GROUP] = bottom; - } - - /** - * Rounds |value| keeping |precision| decimal numbers. Example: 32.76643 - * becomes 32.77. - * @param {number} value The number to round. - * @param {number} precision The desired precision. - * @return {number} The rounded number. - */ - Margins.roundToPrecision = function(value, precision) { - return Math.round(value * Math.pow(10, precision)) / - Math.pow(10, precision); - }; - - Margins.prototype = { - /** - * Checks if |rhs| is equal to |this|. - * @param {Margins} rhs The Margins object to compare against. - * @return {boolean} true if they are equal. - */ - isEqual: function(rhs) { - if (!rhs) - return false; - return this[MarginSettings.TOP_GROUP] === rhs[MarginSettings.TOP_GROUP] && - this[MarginSettings.LEFT_GROUP] === rhs[MarginSettings.LEFT_GROUP] && - this[MarginSettings.RIGHT_GROUP] === - rhs[MarginSettings.RIGHT_GROUP] && - this[MarginSettings.BOTTOM_GROUP] === - rhs[MarginSettings.BOTTOM_GROUP]; - }, - - clone: function() { - return new Margins(this[MarginSettings.LEFT_GROUP], - this[MarginSettings.TOP_GROUP], - this[MarginSettings.RIGHT_GROUP], - this[MarginSettings.BOTTOM_GROUP]); - }, - - /** - * Helper method returning an array of the string indices used for accessing - * all margins. - * @return {array} An array of string indices. - * @private - */ - indicesAsArray_: function() { - return [MarginSettings.LEFT_GROUP, MarginSettings.TOP_GROUP, - MarginSettings.RIGHT_GROUP, MarginSettings.BOTTOM_GROUP]; - }, - - /** - * Rounds |this| based on the precision used when displaying the margins in - * the user's prefered units. This is done by converting from points to - * those units (mm/inches) and back to points. - */ - roundToLocaleUnits: function() { - var indicesAsArray = this.indicesAsArray_(); - for (var i = 0; i < indicesAsArray.length; i++) { - this[indicesAsArray[i]] = - print_preview.convertPointsToLocaleUnitsAndBack( - this[indicesAsArray[i]]); - } - } - }; - - /** - * @constructor - * Class describing the layout of the page. - */ - function PageLayout(width, height, left, top, right, bottom) { - this.contentWidth_ = width; - this.contentHeight_ = height; - this.margins_ = new Margins(left, top, right, bottom); - this.margins_.roundToLocaleUnits(); - } - - PageLayout.prototype = { - /** - * @type {number} The width of the page. - */ - get pageWidth() { - return this.margins_.left + this.margins_.right + this.contentWidth_; - }, - - /** - * @type {number} The height of the page. - */ - get pageHeight() { - return this.margins_.top + this.margins_.bottom + this.contentHeight_; - } - }; - - /** - * Creates a MarginSettings object. This object encapsulates all settings and - * logic related to the margins mode. - * @constructor - */ - function MarginSettings() { - this.marginsOption_ = $('margins-option'); - this.marginList_ = $('margin-list'); - this.marginsUI_ = null; - - // Holds the custom margin values in points (if set). - this.customMargins_ = null; - // Holds the previous custom margin values in points. - this.previousCustomMargins_ = null; - // Holds the width of the page in points. - this.pageWidth_ = -1; - // Holds the height of the page in points. - this.pageHeight_ = -1; - // @type {boolean} True if the margins UI should be diplayed when the next - // |customEvents.PDF_LOADED| event occurs. - this.forceMarginsUIOnPDFLoad_ = false; - // Holds the currently updated default page layout values. - this.currentDefaultPageLayout = null; - - // True if the margins UI should be shown regardless of mouse position. - this.forceDisplayingMarginLines_ = true; - - // @type {EventTracker} Used to keep track of certain event listeners. - this.eventTracker_ = new EventTracker(); - - this.addEventListeners_(); - } - - // Number of points per inch. - MarginSettings.POINTS_PER_INCH = 72; - // Minimum allowed distance in points between top-bottom, left-right margins. - MarginSettings.MINIMUM_MARGINS_DISTANCE = 36; - // Margin list values. Matches enum MarginType in - // printing/print_job_constants.h. - MarginSettings.MARGINS_VALUE_DEFAULT = 0; - MarginSettings.MARGINS_VALUE_NO_MARGINS = 1; - MarginSettings.MARGINS_VALUE_MINIMUM = 2; - MarginSettings.MARGINS_VALUE_CUSTOM = 3; - // Default Margins option index. - MarginSettings.OPTION_INDEX_DEFAULT = 0; - // Group name corresponding to the top margin. - MarginSettings.TOP_GROUP = 'top'; - // Group name corresponding to the left margin. - MarginSettings.LEFT_GROUP = 'left'; - // Group name corresponding to the right margin. - MarginSettings.RIGHT_GROUP = 'right'; - // Group name corresponding to the bottom margin. - MarginSettings.BOTTOM_GROUP = 'bottom'; - - /** - * Extracts the number formatting and measurement system for the current - * locale. - * @param {string} numberFormat Is the formatted version of a sample number - * sent from the backend. - * @param {number} measurementSystem 0 for SI (aka metric system), 1 for the - * system used in the US. Note: Mathces UMeasurementSystem enum in - * third_party/icu/public/i18n/unicode/ulocdata.h. - */ - MarginSettings.setNumberFormatAndMeasurementSystem = function( - numberFormat, measurementSystem) { - var symbols = parseNumberFormat(numberFormat); - MarginSettings.thousandsPoint = symbols[0]; - MarginSettings.decimalPoint = symbols[1]; - MarginSettings.useMetricSystem = measurementSystem == 0; - }; - - cr.addSingletonGetter(MarginSettings); - - MarginSettings.prototype = { - /** - * Returns a dictionary containing the four custom margin values. - * @return {{marginLeft: number, marginTop: number, marginRight: number, - * marginBottom: number}} The dictionary. - */ - get customMargins() { - var margins = {}; - margins.marginLeft = this.customMargins_.left; - margins.marginTop = this.customMargins_.top; - margins.marginRight = this.customMargins_.right; - margins.marginBottom = this.customMargins_.bottom; - return margins; - }, - - /** - * Sets |this.customMargins_| according to |margins|. - * @param {{marginLeft: number, marginTop: number, marginRight: number, - * marginBottom: number}} margins An object holding the four margin - * values. - */ - set customMargins(margins) { - this.customMargins_.left = margins.marginLeft; - this.customMargins_.top = margins.marginTop; - this.customMargins_.right = margins.marginRight; - this.customMargins_.bottom = margins.marginBottom; - }, - - /** - * @return {number} The value of the selected margin option. - */ - get selectedMarginsValue() { - var val = this.marginList_.options[this.marginList_.selectedIndex].value; - return parseInt(val, 10); - }, - - /** - * Sets the current margin selection to |lastUsedMarginType|. - * @param {number} lastUsedMarginType An integer value identifying a margin - * type according to MarginType enum in printing/print_job_constants.h. - * @param {Object} lastUsedCustomMargins The last used custom margins. If - * custom margins have not been used before - * |margin{Top|Bottom|Left|Right}| attributes are missing. - */ - setLastUsedMargins: function(lastUsedMarginsSettings) { - var lastUsedMarginsType = lastUsedMarginsSettings['marginsType']; - this.forceMarginsUIOnPDFLoad_ = - lastUsedMarginsType == MarginSettings.MARGINS_VALUE_CUSTOM; - this.marginList_.selectedIndex = - this.getMarginOptionIndexByValue_(lastUsedMarginsType); - if (lastUsedMarginsSettings.hasOwnProperty('marginTop') && - lastUsedMarginsSettings.hasOwnProperty('marginBottom') && - lastUsedMarginsSettings.hasOwnProperty('marginRight') && - lastUsedMarginsSettings.hasOwnProperty('marginLeft')) { - this.customMargins_ = new Margins(-1, -1, -1 , -1); - this.customMargins = lastUsedMarginsSettings; - } - }, - - /** - * @return {number} The total width of the plugin in points. - */ - get totalWidthInPoints() { - var pageInformation = previewArea.pageLocationNormalized; - return this.pageWidth_ / pageInformation.width; - }, - - /** - * @return {number} The total height of the plugin in points. - */ - get totalHeightInPoints() { - var pageInformation = previewArea.pageLocationNormalized; - return this.pageHeight_ / pageInformation.height; - }, - - /** - * Maps margin type values to indices within |this.marginList_|. - * @param {number} marginTypeValue An integer value identifying a margin - * type according to MarginType enum in printing/print_job_constants.h. - * @return {number} The index within |this.marginList_| that corrsponds to - * |marginTypeValue|. - * @private - */ - getMarginOptionIndexByValue_: function(marginTypeValue) { - var options = this.marginList_.options; - for (var i = 0; i < options.length; i++) { - if (options[i].getAttribute('value') == marginTypeValue) - return i; - } - return MarginSettings.OPTION_INDEX_DEFAULT; - }, - - /** - * @return {boolean} True if default margins are selected. - */ - isDefaultMarginsSelected: function() { - return this.selectedMarginsValue == MarginSettings.MARGINS_VALUE_DEFAULT; - }, - - /** - * @return {boolean} True if no margins are selected. - */ - isNoMarginsSelected: function() { - return this.selectedMarginsValue == - MarginSettings.MARGINS_VALUE_NO_MARGINS; - }, - - /** - * @return {boolean} True if custom margins are selected. - */ - isCustomMarginsSelected: function() { - return this.selectedMarginsValue == MarginSettings.MARGINS_VALUE_CUSTOM; - }, - - /** - * @return {boolean} True if minimum margins are selected. - */ - isMinimumMarginsSelected: function() { - return this.selectedMarginsValue == MarginSettings.MARGINS_VALUE_MINIMUM; - }, - - /** - * If the custom margin values have changed then request a new preview based - * on the newly set margins. - * @private - */ - requestPreviewIfNeeded_: function() { - if (!this.areMarginSettingsValid()) - return; - - if (this.customMargins_.isEqual(this.previousCustomMargins_)) - return; - - this.previousCustomMargins_ = this.customMargins_.clone(); - setDefaultValuesAndRegeneratePreview(false); - }, - - /** - * Listener executed when the mouse is over the sidebar. If the custom - * margin lines are displayed, then, it fades them out. - * @private - */ - onSidebarMouseOver_: function(e) { - $('mainview').onmouseover = this.onMainviewMouseOver_.bind(this); - $('navbar-container').onmouseover = null; - if (!this.forceDisplayingMarginLines_) - this.marginsUI.hide(false); - }, - - /** - * Listener executed when the mouse is over the main view. If the custom - * margin lines are hidden, then, it fades them in. - * @private - */ - onMainviewMouseOver_: function() { - $('mainview').onmouseover = null; - $('navbar-container').onmouseover = this.onSidebarMouseOver_.bind(this); - this.forceDisplayingMarginLines_ = false; - this.marginsUI.show(); - }, - - /** - * Adds listeners to all margin related controls. - * @private - */ - addEventListeners_: function() { - this.marginList_.onchange = this.onMarginsChanged_.bind(this); - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - document.addEventListener(customEvents.PDF_GENERATION_ERROR, - this.onPDFGenerationError_.bind(this)); - }, - - /** - * Executes when a |customEvents.PDF_GENERATION_ERROR| event occurs. - * @private - */ - onPDFGenerationError_: function() { - if (this.isCustomMarginsSelected()) { - this.removeCustomMarginEventListeners_(); - this.marginsUI.hide(true); - } - }, - - /** - * Executes whenever a |customEvents.MARGIN_LINE_DRAG| occurs. - * @param {cr.Event} e The event that triggered this listener. - */ - onMarginsLineDrag_: function(e) { - var dragDeltaInPoints = this.convertDragDeltaToPoints_(e.dragDelta); - this.marginsUI.lastClickedMarginsUIPair.updateWhileDragging( - dragDeltaInPoints, e.destinationPoint); - }, - - /** - * @param {number} dragDelta The difference in pixels between the original - * and current postion of the last clicked margin line. - * @return {number} The difference in points. - * @private - */ - convertDragDeltaToPoints_: function(dragDelta) { - if (this.marginsUI.lastClickedMarginsUIPair.isTop_() || - this.marginsUI.lastClickedMarginsUIPair.isBottom_()) { - return dragDelta * this.totalHeightInPoints; - } else { - return dragDelta * this.totalWidthInPoints; - } - }, - - /** - * @return {boolean} True if the margin settings are valid. - */ - areMarginSettingsValid: function() { - if (!this.isCustomMarginsSelected() || !this.marginsUI_) - return true; - - var pairs = this.marginsUI.pairsAsList; - return pairs.every(function(pair) { return pair.box_.isValid; }); - }, - - /** - * Calculates the maximum allowable value of the selected margin text for - * every margin. - * @return {array} The maximum allowable value in points in order top, left, - * right, bottom. - * @private - */ - getMarginValueLimits_: function() { - var marginValueLimits = []; - marginValueLimits[0] = this.pageHeight_ - this.customMargins_.bottom - - MarginSettings.MINIMUM_MARGINS_DISTANCE; - marginValueLimits[1] = this.pageWidth_ - this.customMargins_.right - - MarginSettings.MINIMUM_MARGINS_DISTANCE; - marginValueLimits[2] = this.pageWidth_ - this.customMargins_.left - - MarginSettings.MINIMUM_MARGINS_DISTANCE; - marginValueLimits[3] = this.pageHeight_ - this.customMargins_.top - - MarginSettings.MINIMUM_MARGINS_DISTANCE; - - for (var i = 0; i < marginValueLimits.length; i++) { - marginValueLimits[i] = Math.max(marginValueLimits[i], 0); - marginValueLimits[i] = print_preview.convertPointsToLocaleUnitsAndBack( - marginValueLimits[i]); - } - return marginValueLimits; - }, - - /** - * @return {array} The margin value limits positions normalized to the total - * width and height of the plugin and with respect to the top left - * corner of the plugin. - */ - getMarginValueLimitsInPercent_: function() { - var pageInformation = previewArea.pageLocationNormalized; - var totalWidthInPoints = this.pageWidth_ / pageInformation.width; - var totalHeightInPoints = this.pageHeight_ / pageInformation.height; - var marginValueLimits = this.getMarginValueLimits_(); - var marginValueLimitsInPercent = []; - marginValueLimitsInPercent[0] = pageInformation.y + marginValueLimits[0] / - totalHeightInPoints; - marginValueLimitsInPercent[1] = pageInformation.x + marginValueLimits[1] / - totalWidthInPoints; - marginValueLimitsInPercent[2] = pageInformation.x + - pageInformation.width - marginValueLimits[2] / totalWidthInPoints; - marginValueLimitsInPercent[3] = pageInformation.y + - pageInformation.height - marginValueLimits[3] / totalHeightInPoints; - return marginValueLimitsInPercent; - }, - - /** - * When the user stops typing in the margin text box a new print preview is - * requested, only if - * 1) The input is compeletely valid (it can be parsed in its entirety). - * 2) The newly selected margins differ from the previously selected. - * @param {cr.Event} event The change event holding information about what - * changed. - * @private - */ - onMarginTextValueMayHaveChanged_: function(event) { - var marginBox = event.target; - var marginBoxValue = - print_preview.convertLocaleUnitsToPoints(marginBox.margin); - this.customMargins_[marginBox.marginGroup] = marginBoxValue; - this.requestPreviewIfNeeded_(); - }, - - /** - * @type {print_preview.MarginsUI} The object holding the UI for specifying - * custom margins. - */ - get marginsUI() { - if (!this.marginsUI_) { - this.marginsUI_ = new print_preview.MarginsUI(); - $('mainview').appendChild(this.marginsUI_); - this.marginsUI_.addObserver( - this.onMarginTextValueMayHaveChanged_.bind(this)); - } - return this.marginsUI_; - }, - - /** - * Adds listeners when the custom margins option is selected. - * @private - */ - addCustomMarginEventListeners_: function() { - $('mainview').onmouseover = this.onMainviewMouseOver_.bind(this); - $('navbar-container').onmouseover = this.onSidebarMouseOver_.bind(this); - this.eventTracker_.add(this.marginsUI, - customEvents.MARGIN_LINE_DRAG, - this.onMarginsLineDrag_.bind(this), - false); - this.eventTracker_.add(document, customEvents.MARGIN_TEXTBOX_FOCUSED, - this.onMarginTextboxFocused_.bind(this), false); - }, - - /** - * Removes the event listeners associated with the custom margins option. - * @private - */ - removeCustomMarginEventListeners_: function() { - if (!this.marginsUI_) - return; - $('mainview').onmouseover = null; - $('navbar-container').onmouseover = null; - this.eventTracker_.remove(this.marginsUI, customEvents.MARGIN_LINE_DRAG); - this.eventTracker_.remove(document, customEvents.MARGIN_TEXTBOX_FOCUSED); - this.marginsUI.hide(true); - }, - - /** - * Updates |this.marginsUI| depending on the specified margins and the - * position of the page within the plugin. - * @private - */ - drawCustomMarginsUI_: function() { - // TODO(dpapad): find out why passing |!this.areMarginsSettingsValid()| - // directly produces the opposite value even though - // |this.getMarginsRectangleInPercent_()| and - // |this.getMarginValueLimits_()| have no side effects. - previewArea.update(); - var keepDisplayedValue = !this.areMarginSettingsValid(); - this.marginsUI.update(this.getMarginsRectangleInPercent_(), - this.customMargins_, - this.getMarginValueLimits_(), - keepDisplayedValue, - this.getMarginValueLimitsInPercent_()); - this.marginsUI.draw(); - }, - - /** - * Called when there is change in the preview position or size. - */ - onPreviewPositionChanged: function() { - if (!previewArea.pdfPlugin) - return; - if (this.isCustomMarginsSelected() && previewArea.pdfLoaded && - pageSettings.totalPageCount != undefined) { - this.updatePageData_(); - this.drawCustomMarginsUI_(); - } - }, - - /** - * Executes when a margin textbox is focused. Used for improved - * accessibility. - * @private - */ - onMarginTextboxFocused_: function() { - this.forceDisplayingMarginLines_ = true; - this.marginsUI.show(); - }, - - /** - * Executes when user selects a different margin option, ie, - * |this.marginList_.selectedIndex| is changed. - * @private - */ - onMarginsChanged_: function() { - if (this.isDefaultMarginsSelected() || this.isMinimumMarginsSelected() || - this.isNoMarginsSelected()) - this.onDefaultMinimumNoMarginsSelected_(); - else if (this.isCustomMarginsSelected()) - this.onCustomMarginsSelected_(); - }, - - /** - * Executes when the default or minimum or no margins option is selected. - * @private - */ - onDefaultMinimumNoMarginsSelected_: function() { - this.removeCustomMarginEventListeners_(); - this.forceDisplayingMarginLines_ = true; - this.previousCustomMargins_ = null; - setDefaultValuesAndRegeneratePreview(false); - }, - - /** - * Executes when the custom margins option is selected. - * @private - */ - onCustomMarginsSelected_: function() { - if (!previewArea.pdfPlugin) { - this.forceMarginsUIOnPDFLoad_ = true; - return; - } - var customMarginsNotSpecified = !this.customMargins_; - this.updatePageData_(); - - if (customMarginsNotSpecified) { - this.previousCustomMargins_ = this.customMargins_.clone(); - this.drawCustomMarginsUI_(); - this.addCustomMarginEventListeners_(); - this.marginsUI.show(); - } else { - this.forceMarginsUIOnPDFLoad_ = true; - this.requestPreviewIfNeeded_(); - } - }, - - /** - * Calculates the coordinates of the four margin lines. These are the - * coordinates where the margin lines should be displayed. The coordinates - * are expressed in terms of percentages with respect to the total width - * and height of the plugin. - * @return {print_preview.Rect} A rectnangle that describes the position of - * the four margin lines. - * @private - */ - getMarginsRectangleInPercent_: function() { - var pageLocation = previewArea.pageLocationNormalized; - var marginsInPercent = this.getMarginsInPercent_(); - var leftX = pageLocation.x + marginsInPercent.left; - var topY = pageLocation.y + marginsInPercent.top; - var contentWidth = pageLocation.width - (marginsInPercent.left + - marginsInPercent.right); - var contentHeight = pageLocation.height - (marginsInPercent.top + - marginsInPercent.bottom); - return new print_preview.Rect( - leftX, topY, contentWidth, contentHeight); - }, - - /** - * @return {print_preview.Margins} The currently selected margin values - * normalized to the total width and height of the plugin. - * @private - */ - getMarginsInPercent_: function() { - return this.convertMarginsInPointsToPercent(this.customMargins_); - }, - - /** - * Converts |marginsToConvert| to points and normalizes it to the height and - * width of the plugin. - * @return {print_preview.Margins} The margins in percent. - * @private - */ - convertMarginsInPointsToPercent: function(marginsToConvert) { - var pageInformation = previewArea.pageLocationNormalized; - var totalWidthInPoints = this.pageWidth_ / pageInformation.width; - var totalHeightInPoints = this.pageHeight_ / pageInformation.height; - var marginsInPercent = new Margins( - marginsToConvert.left / totalWidthInPoints, - marginsToConvert.top / totalHeightInPoints, - marginsToConvert.right / totalWidthInPoints, - marginsToConvert.bottom / totalHeightInPoints); - return marginsInPercent; - }, - - /** - * If custom margins is the currently selected option then change to the - * default margins option. - * @private - */ - resetMarginsIfNeeded: function() { - if (this.isCustomMarginsSelected()) { - this.marginList_.options[ - MarginSettings.OPTION_INDEX_DEFAULT].selected = true; - this.removeCustomMarginEventListeners_(); - this.forceDisplayingMarginLines_ = true; - this.customMargins_ = null; - this.previousCustomMargins_ = null; - } - }, - - /** - * Executes when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - if (!previewModifiable) { - fadeOutOption(this.marginsOption_); - return; - } - - if (this.forceMarginsUIOnPDFLoad_) { - this.updatePageData_(); - this.drawCustomMarginsUI_(); - this.addCustomMarginEventListeners_(); - this.marginsUI.show(); - this.forceMarginsUIOnPDFLoad_ = false; - } - }, - - /** - * Updates |this.customMargins_|, |this.pageWidth_|, |this.pageHeight_|. - * @private - */ - updatePageData_: function() { - if (!this.customMargins_) - this.customMargins_ = this.currentDefaultPageLayout.margins_.clone(); - - this.pageWidth_ = this.currentDefaultPageLayout.pageWidth; - this.pageHeight_ = this.currentDefaultPageLayout.pageHeight; - } - }; - - return { - MarginSettings: MarginSettings, - PageLayout: PageLayout - }; -}); diff --git a/chrome/browser/resources/print_preview/margin_textbox.js b/chrome/browser/resources/print_preview/margin_textbox.js deleted file mode 100644 index f0496f5..0000000 --- a/chrome/browser/resources/print_preview/margin_textbox.js +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - function MarginTextbox(groupName) { - var box = document.createElement('input'); - box.__proto__ = MarginTextbox.prototype; - box.setAttribute('type', 'text'); - box.className = MarginTextbox.CSS_CLASS_MARGIN_TEXTBOX; - box.value = '0'; - box.setAttribute('aria-label', localStrings.getString(groupName)); - - // @type {string} Specifies which margin this line refers to. - box.marginGroup = groupName; - // @type {boolean} True if the displayed value is valid. - box.isValid = true; - // @type {number} Timer used to detect when the user stops typing. - box.timerId_ = null; - // @type {number} The last valid value in points. - box.lastValidValueInPoints = 0; - // @type {number} The upper allowed limit for the corresponding margin. - box.valueLimit = null; - - box.addEventListeners_(); - return box; - } - - MarginTextbox.CSS_CLASS_MARGIN_TEXTBOX = 'margin-box'; - // Keycode for the "Escape" key. - MarginTextbox.ESCAPE_KEYCODE = 27; - - MarginTextbox.prototype = { - __proto__: HTMLInputElement.prototype, - - /** - * @type {number} The margin value currently in the textbox. - */ - get margin() { - return print_preview.extractMarginValue(this.value); - }, - - /** - * Sets the contents of the textbox. - * @param {number} newValueInPoints The value to be displayed in points. - * @private - */ - setValue_: function(newValueInPoints) { - this.value = - print_preview.convertPointsToLocaleUnitsText(newValueInPoints); - }, - - /** - * Updates the state of |this|. - * @param {number} value The margin value in points. - * @param {number} valueLimit The upper allowed value for the margin. - * @param {boolean} keepDisplayedValue True if the currently displayed value - * should not be updated. - */ - update: function(value, valueLimit, keepDisplayedValue) { - this.lastValidValueInPoints = value; - if (!keepDisplayedValue) - this.setValue_(this.lastValidValueInPoints); - - this.valueLimit = valueLimit; - this.validate(); - }, - - /** - * Updates |this| while dragging is in progress. - * @param {number} dragDeltaInPoints The difference in points between the - * margin value before dragging started and now. - */ - updateWhileDragging: function(dragDeltaInPoints) { - var validity = this.validateDelta(dragDeltaInPoints); - - if (validity == print_preview.marginValidationStates.WITHIN_RANGE) - this.setValue_(this.lastValidValueInPoints + dragDeltaInPoints); - else if (validity == print_preview.marginValidationStates.TOO_SMALL) - this.setValue_(0); - else if (validity == print_preview.marginValidationStates.TOO_BIG) - this.setValue_(this.valueLimit); - - this.validate(); - this.updateColor(); - }, - - /** - * @param {number} dragDeltaInPoints The difference in points between the - * margin value before dragging started and now. - * @return {number} An appropriate value from enum |marginValidationStates|. - */ - validateDelta: function(dragDeltaInPoints) { - var newValue = this.lastValidValueInPoints + dragDeltaInPoints; - return print_preview.validateMarginValue(newValue, this.valueLimit); - }, - - /** - * Updates |this.isValid|. - */ - validate: function() { - this.isValid = - print_preview.validateMarginText(this.value, this.valueLimit) == - print_preview.marginValidationStates.WITHIN_RANGE; - }, - - /** - * Updates the background color depending on |isValid| by adding/removing - * the appropriate CSS class. - * @param {boolean} isValid True if the margin is valid. - */ - updateColor: function() { - this.isValid ? this.classList.remove('invalid') : - this.classList.add('invalid'); - }, - - /** - * Draws this textbox. - */ - draw: function() { - this.updateColor(); - }, - - /** - * Adds event listeners for various events. - * @private - */ - addEventListeners_: function() { - this.oninput = this.resetTimer_.bind(this); - this.onblur = this.onBlur_.bind(this); - this.onkeypress = this.onKeyPressed_.bind(this); - this.onkeyup = this.onKeyUp_.bind(this); - this.onfocus = function() { - cr.dispatchSimpleEvent(document, customEvents.MARGIN_TEXTBOX_FOCUSED); - }; - }, - - /** - * Executes whenever a blur event occurs. - * @private - */ - onBlur_: function() { - clearTimeout(this.timerId_); - this.validate(); - if (!this.isValid) { - this.setValue_(this.lastValidValueInPoints); - this.validate(); - } - - this.updateColor(); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - cr.dispatchSimpleEvent(this, customEvents.MARGINS_MAY_HAVE_CHANGED); - }, - - /** - * Executes whenever a keypressed event occurs. Note: Only the "Enter" key - * event is handled. The "Escape" key does not result in such event, - * therefore it is handled by |this.onKeyUp_|. - * @param {KeyboardEvent} e The event that triggered this listener. - * @private - */ - onKeyPressed_: function(e) { - if (e.keyIdentifier == 'Enter') - this.blur(); - }, - - /** - * Executes whenever a keyup event occurs. Note: Only the "Escape" - * key event is handled. - * @param {KeyboardEvent} e The event that triggered this listener. - * @private - */ - onKeyUp_: function(e) { - if (e.keyCode == MarginTextbox.ESCAPE_KEYCODE) { - this.setValue_(this.lastValidValueInPoints); - this.validate(); - this.updateColor(); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - } - }, - - /** - * Resetting the timer used to detect when the user stops typing in order - * to update the print preview. - * @private - */ - resetTimer_: function() { - clearTimeout(this.timerId_); - this.timerId_ = window.setTimeout( - this.onTextValueMayHaveChanged.bind(this), 1000); - }, - - /** - * Executes whenever the user stops typing or when a drag session associated - * with |this| ends. - */ - onTextValueMayHaveChanged: function() { - this.validate(); - this.updateColor(); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - - if (!this.isValid) - return; - cr.dispatchSimpleEvent(this, customEvents.MARGINS_MAY_HAVE_CHANGED); - } - - }; - - return { - MarginTextbox: MarginTextbox - }; -}); diff --git a/chrome/browser/resources/print_preview/margin_utils.js b/chrome/browser/resources/print_preview/margin_utils.js deleted file mode 100644 index a93ef0e..0000000 --- a/chrome/browser/resources/print_preview/margin_utils.js +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Checks if |text| has a valid margin value format. A valid format is - * parsable as a number and is greater than zero. - * Example: "1.00", "1", ".5", "1.1" are valid values. - * Example: "1.4dsf", "-1" are invalid. - * Note: The inch symbol (") at the end of |text| is allowed. - * - * @param {string} text The text to check. - * @return {number} The margin value represented by |text| or null if |text| - * does not represent a valid number. - */ - function extractMarginValue(text) { - // Removing whitespace anywhere in the string. - text = text.replace(/\s*/g, ''); - if (text.length == 0) - return -1; - var validationRegex = getValidationRegExp(); - if (validationRegex.test(text)) { - // Replacing decimal point with the dot symbol in order to use - // parseFloat() properly. - var replacementRegex = new RegExp('\\' + - print_preview.MarginSettings.decimalPoint + '{1}'); - text = text.replace(replacementRegex, '.'); - return parseFloat(text); - } - return -1; - } - - /** - * @return {RegExp} A regular expression for validating the input of the user. - * It takes into account the user's locale. - */ - function getValidationRegExp() { - var regex = new RegExp('(^\\d+)(\\' + - print_preview.MarginSettings.thousandsPoint + '\\d{3})*(\\' + - print_preview.MarginSettings.decimalPoint + '\\d+)*' + - (!print_preview.MarginSettings.useMetricSystem ? '"?' : '(mm)?') + '$'); - return regex; - } - - var marginValidationStates = { - TOO_SMALL: 0, - WITHIN_RANGE: 1, - TOO_BIG: 2, - NOT_A_NUMBER: 3 - }; - - /** - * Checks whether |value| is within range [0, limit]. - * @return {number} An appropriate value from enum |marginValidationStates|. - */ - function validateMarginValue(value, limit) { - if (value <= limit && value >= 0) - return marginValidationStates.WITHIN_RANGE; - else if (value < 0) - return marginValidationStates.TOO_SMALL; - else - return marginValidationStates.TOO_BIG; - } - - /** - * @param {string} text The text to check in user's locale units. - * @param {number} limit The upper bound of the valid margin range (in - * points). - * @return {number} An appropriate value from enum |marginValidationStates|. - */ - function validateMarginText(text, limit) { - var value = extractMarginValue(text); - if (value == -1) - return marginValidationStates.NOT_A_NUMBER; - value = print_preview.convertLocaleUnitsToPoints(value); - return validateMarginValue(value, limit); - } - - /** - * @param {number} value The value to convert in points. - * @return {number} The corresponding value after converting to user's locale - * units. - */ - function convertPointsToLocaleUnits(value) { - return print_preview.MarginSettings.useMetricSystem ? - convertPointsToMillimeters(value) : convertPointsToInches(value); - } - - /** - * @param {number} value The value to convert in user's locale units. - * @return {number} The corresponding value after converting to points. - */ - function convertLocaleUnitsToPoints(value) { - return print_preview.MarginSettings.useMetricSystem ? - convertMillimetersToPoints(value) : convertInchesToPoints(value); - } - - /** - * Converts |value| to user's locale units and then back to points. Note: - * Because of the precision the return value might be different than |value|. - * @param {number} value The value in points to convert. - * @return {number} The value in points after converting to user's locale - * units with a certain precision and back. - */ - function convertPointsToLocaleUnitsAndBack(value) { - var inLocaleUnits = - convertPointsToLocaleUnits(value).toFixed(getDesiredPrecision()); - return convertLocaleUnitsToPoints(inLocaleUnits); - } - - /** - * @return {number} The number of decimal digits to keep based on the - * measurement system used. - */ - function getDesiredPrecision() { - return print_preview.MarginSettings.useMetricSystem ? 0 : 2; - } - - /** - * @param {number} value The value to convert in points. - * @return {string} The equivalent text in user locale units with precision - * of two digits. - */ - function convertPointsToLocaleUnitsText(value) { - var inLocaleUnits = - convertPointsToLocaleUnits(value).toFixed(getDesiredPrecision()); - var inLocaleUnitsText = inLocaleUnits.toString(10).replace( - /\./g, print_preview.MarginSettings.decimalPoint); - return !print_preview.MarginSettings.useMetricSystem ? - inLocaleUnitsText + '"' : inLocaleUnitsText + 'mm'; - } - - /** - * Creates a Rect object. This object describes a rectangle in a 2D plane. The - * units of |x|, |y|, |width|, |height| are chosen by clients of this class. - * @constructor - */ - function Rect(x, y, width, height) { - // @type {number} Horizontal distance of the upper left corner from origin. - this.x = x; - // @type {number} Vertical distance of the upper left corner from origin. - this.y = y; - // @type {number} Width of |this| rectangle. - this.width = width; - // @type {number} Height of |this| rectangle. - this.height = height; - }; - - Rect.prototype = { - /** - * @type {number} The x coordinate of the right-most point. - */ - get right() { - return this.x + this.width; - }, - - /** - * @type {number} The y coordinate of the lower-most point. - */ - get bottom() { - return this.y + this.height; - }, - - /** - * Clones |this| and returns the cloned object. - * @return {Rect} A copy of |this|. - */ - clone: function() { - return new Rect(this.x, this.y, this.width, this.height); - } - }; - - return { - convertPointsToLocaleUnitsAndBack: - convertPointsToLocaleUnitsAndBack, - convertPointsToLocaleUnitsText: convertPointsToLocaleUnitsText, - convertLocaleUnitsToPoints: convertLocaleUnitsToPoints, - extractMarginValue: extractMarginValue, - marginValidationStates: marginValidationStates, - Rect: Rect, - validateMarginText: validateMarginText, - validateMarginValue: validateMarginValue - }; -}); diff --git a/chrome/browser/resources/print_preview/margins.css b/chrome/browser/resources/print_preview/margins.css deleted file mode 100644 index f2fd789..0000000 --- a/chrome/browser/resources/print_preview/margins.css +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright (c) 2012 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -#print-preview .margin-box { - background-color: #2a2a2a; - border: 1px solid #888; - box-sizing: border-box; - color: white; - cursor: auto; - font-family: arial; - font-size: 0.8em; - height: 25px; - left: 50%; - padding: 5px 10px; - position: absolute; - text-align: center; - top: 50%; - width: 60px; -} - -.margins-ui-pair.top .margin-box, -.margins-ui-pair.bottom .margin-box { - /* -width / 2 + 1 for the margin border */ - margin-left: -30px; -} - -.margins-ui-pair.left .margin-box, -.margins-ui-pair.right .margin-box { - /* -height / 2 */ - margin-top: -12px; -} - -.margins-ui-pair.bottom .margin-box { - /* -height + 1 */ - margin-top: -23px; -} - -.margins-ui-pair.right .margin-box { - /* -width - 2 */ - margin-left: -58px; -} - -.margin-box.invalid { - background-color: rgb(193, 27, 23); -} - -.margins-ui-pair { - background-color: transparent; - border-color: transparent; - position: absolute; -} - -.margins-ui-pair.right, -.margins-ui-pair.left { - cursor: ew-resize; -} - -.margins-ui-pair.top, -.margins-ui-pair.bottom { - cursor: ns-resize; -} - -.margins-ui-pair.dragging { - z-index: 1; -} - -.margin-line { - border-color: rgb(64, 128, 250); - border-style: dashed; - border-width: 1px; - pointer-events: none; - position: absolute; -} - -.margins-ui-pair.top .margin-line, -.margins-ui-pair.bottom .margin-line { - height: 0; - left: 0; - top: 50%; - width: 100%; -} - -.margins-ui-pair.left .margin-line, -.margins-ui-pair.right .margin-line { - height: 100%; - left: 50%; - top: 0; - width: 0; -} - -#customized-margins { - position: absolute; - top: 0; -} - -#customized-margins.invisible { - opacity: 0; - pointer-events: none; -} diff --git a/chrome/browser/resources/print_preview/margins_ui.js b/chrome/browser/resources/print_preview/margins_ui.js deleted file mode 100644 index c2d5309..0000000 --- a/chrome/browser/resources/print_preview/margins_ui.js +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - function MarginsUI() { - var marginsUI = document.createElement('div'); - marginsUI.__proto__ = MarginsUI.prototype; - marginsUI.id = 'customized-margins'; - - // @type {print_preview.MarginsUIPair} The object corresponding to the top - // margin. - marginsUI.topPair_ = new print_preview.MarginsUIPair( - print_preview.MarginSettings.TOP_GROUP); - // @type {print_preview.MarginsUIPair} The object corresponding to the left - // margin. - marginsUI.leftPair_ = new print_preview.MarginsUIPair( - print_preview.MarginSettings.LEFT_GROUP); - // @type {print_preview.MarginsUIPair} The object corresponding to the right - // margin. - marginsUI.rightPair_ = new print_preview.MarginsUIPair( - print_preview.MarginSettings.RIGHT_GROUP); - // @type {print_preview.MarginsUIPair} The object corresponding to the - // bottom margin. - marginsUI.bottomPair_ = new print_preview.MarginsUIPair( - print_preview.MarginSettings.BOTTOM_GROUP); - - var uiPairs = marginsUI.pairsAsList; - for (var i = 0; i < uiPairs.length; i++) - marginsUI.appendChild(uiPairs[i]); - - // @type {print_preview.MarginsUIPair} The object that is being dragged. - // null if no drag session is in progress. - marginsUI.lastClickedMarginsUIPair = null; - - // @type {EventTracker} Used to keep track of certain event listeners. - marginsUI.eventTracker_ = new EventTracker(); - - marginsUI.addEventListeners_(); - return marginsUI; - } - - /** - * @param {{x: number, y: number}} point The point with respect to the top - * left corner of the webpage. - * @return {{x: number, y: number}} The point with respect to the top left - * corner of the plugin area. - */ - MarginsUI.convert = function(point) { - var mainview = $('mainview'); - return { x: point.x - mainview.offsetLeft, - y: point.y - mainview.offsetTop }; - } - - MarginsUI.prototype = { - __proto__: HTMLDivElement.prototype, - - /** - * Adds an observer for |customEvents.MARGINS_MAY_HAVE_CHANGED| event. - * @param {function} func A callback function to be called when - * |customEvents.MARGINS_MAY_HAVE_CHANGED| event occurs. - */ - addObserver: function(func) { - var uiPairs = this.pairsAsList; - for (var i = 0; i < uiPairs.length; i++) { - uiPairs[i].box_.addEventListener( - customEvents.MARGINS_MAY_HAVE_CHANGED, func); - } - }, - - /** - * @return {array} An array including all |MarginUIPair| objects. - */ - get pairsAsList() { - return [this.topPair_, this.leftPair_, this.rightPair_, this.bottomPair_]; - }, - - /** - * Updates the state of the margins UI. - * @param {print_preview.Rect} marginsRectangle A rectangle describing the - * four margins. - * @param {Margins} marginValues The margin values in points. - * @param {Array.<number>} valueLimits The maximum allowed margins for each - * side in points. - * @param {boolean} keepDisplayedValue True if the currently displayed - * margin values should not be updated. - * @param {Array.<number>} valueLimitsInPercent The maximum allowed margins - * for each side in percentages. - */ - update: function(marginsRectangle, marginValues, valueLimits, - keepDisplayedValue, valueLimitsInPercent) { - var uiPairs = this.pairsAsList; - var order = ['top', 'left', 'right', 'bottom']; - for (var i = 0; i < uiPairs.length; i++) { - uiPairs[i].update(marginsRectangle, - marginValues[order[i]], - valueLimits[i], - keepDisplayedValue, - valueLimitsInPercent[i]); - } - }, - - /** - * Draws |this| based on the latest state. - */ - draw: function() { - this.applyClippingMask_(); - this.pairsAsList.forEach(function(pair) { pair.draw(); }); - }, - - /** - * Shows the margins UI. - */ - show: function() { - this.hidden = false; - this.classList.remove('invisible'); - }, - - /** - * Hides the margins UI and removes from the rendering flow if requested. - * @param {boolean} removeFromFlow True if |this| should also be removed - * from the rendering flow (in order to not interfere with the tab - * order). - */ - hide: function(removeFromFlow) { - removeFromFlow ? this.hidden = true : this.classList.add('invisible'); - }, - - /** - * Applies a clipping mask on |this| so that it does not paint on top of the - * scrollbars (if any). - */ - applyClippingMask_: function() { - var bottom = previewArea.height; - var right = previewArea.width; - this.style.clip = 'rect(0, ' + right + 'px, ' + bottom + 'px, 0)'; - }, - - /** - * Adds event listeners for various events. - * @private - */ - addEventListeners_: function() { - var uiPairs = this.pairsAsList; - for (var i = 0; i < uiPairs.length; i++) { - uiPairs[i].addEventListener(customEvents.MARGIN_LINE_MOUSE_DOWN, - this.onMarginLineMouseDown.bind(this)); - } - // After snapping to min/max the MarginUIPair might not receive the - // mouseup event since it is not under the mouse pointer, so it is handled - // here. - window.document.addEventListener('mouseup', - this.onMarginLineMouseUp.bind(this)); - }, - - /** - * Executes when a "MarginLineMouseDown" event occurs. - * @param {cr.Event} e The event that triggered this listener. - */ - onMarginLineMouseDown: function(e) { - this.lastClickedMarginsUIPair = e.target; - this.bringToFront(this.lastClickedMarginsUIPair); - // Note: Capturing mouse events at a higher level in the DOM than |this|, - // so that the plugin can still receive mouse events. - this.eventTracker_.add( - window.document, 'mousemove', this.onMouseMove_.bind(this), false); - }, - - /** - * Executes when a "MarginLineMouseUp" event occurs. - * @param {cr.Event} e The event that triggered this listener. - */ - onMarginLineMouseUp: function(e) { - if (this.lastClickedMarginsUIPair == null) - return; - this.lastClickedMarginsUIPair.onMouseUp(); - this.lastClickedMarginsUIPair = null; - this.eventTracker_.remove(window.document, 'mousemove'); - }, - - /** - * Brings |uiPair| in front of the other pairs. Used to make sure that the - * dragged pair is visible when overlapping with a not dragged pair. - * @param {print_preview.MarginsUIPair} uiPair The pair to bring in front. - */ - bringToFront: function(uiPair) { - this.pairsAsList.forEach(function(pair) { - pair.classList.remove('dragging'); - }); - uiPair.classList.add('dragging'); - }, - - /** - * Executes when a mousemove event occurs. - * @param {MouseEvent} e The event that triggered this listener. - */ - onMouseMove_: function(e) { - var point = MarginsUI.convert({ x: e.x, y: e.y }); - - var dragEvent = new cr.Event(customEvents.MARGIN_LINE_DRAG); - dragEvent.dragDelta = - this.lastClickedMarginsUIPair.getDragDisplacementFrom(point); - dragEvent.destinationPoint = point; - this.dispatchEvent(dragEvent); - } - }; - - return { - MarginsUI: MarginsUI - }; -}); diff --git a/chrome/browser/resources/print_preview/margins_ui_pair.js b/chrome/browser/resources/print_preview/margins_ui_pair.js deleted file mode 100644 index e35e9f2..0000000 --- a/chrome/browser/resources/print_preview/margins_ui_pair.js +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * @constructor - * This class represents a margin line and a textbox corresponding to that - * margin. - */ - function MarginsUIPair(groupName) { - var marginsUIPair = document.createElement('div'); - marginsUIPair.__proto__ = MarginsUIPair.prototype; - marginsUIPair.className = MarginsUIPair.CSS_CLASS; - // @type {string} Specifies which margin this line refers to. - marginsUIPair.marginGroup = groupName; - marginsUIPair.classList.add(groupName); - - // @type {print_preview.Rect} A rectangle describing the dimensions of - // the draggable area. - marginsUIPair.rectangle = null; - // @type {print_preview.Rect} A rectangle describing the four margins. - marginsUIPair.marginsRectangle = null; - // @type {HTMLDivElement} The line representing the margin. - marginsUIPair.line_ = document.createElement('div'); - marginsUIPair.line_.className = 'margin-line'; - // @type {print_preview.MarginTextbox} The textbox corresponding to this - // margin. - marginsUIPair.box_ = new print_preview.MarginTextbox(groupName); - // @type {{x: number, y: number}} The point where mousedown occured within - // the draggable area with respect the top-left corner of |this|. - marginsUIPair.mousePointerOffset = null; - // @type {{x: number, y:number}} The position of the origin before any - // dragging in progress occured. - marginsUIPair.originBeforeDragging = null; - - marginsUIPair.appendChild(marginsUIPair.line_); - marginsUIPair.appendChild(marginsUIPair.box_); - - marginsUIPair.addEventListeners_(); - return marginsUIPair; - } - - MarginsUIPair.CSS_CLASS = 'margins-ui-pair'; - // Width of the clickable region around each margin line in screen pixels. - MarginsUIPair.CLICKABLE_REGION = 20; - - MarginsUIPair.prototype = { - __proto__: HTMLDivElement.prototype, - - /** - * Updates the state. - */ - update: function(marginsRectangle, value, valueLimit, keepDisplayedValue, - valueLimitInPercent) { - this.marginsRectangle = marginsRectangle; - this.valueLimitInPercent = valueLimitInPercent; - this.rectangle = this.getCoordinates_(this.marginsRectangle); - this.box_.update(value, valueLimit, keepDisplayedValue); - }, - - /** - * Draws |this| based on the state. - */ - draw: function() { - this.drawDraggableArea_(); - this.box_.draw(); - }, - - /** - * Draws the area that can be clicked in order to start dragging. - * @private - */ - drawDraggableArea_: function() { - var width = previewArea.pdfPlugin_.offsetWidth; - var height = previewArea.pdfPlugin_.offsetHeight; - - this.style.left = Math.round(this.rectangle.x * width) + 'px'; - this.style.top = Math.round(this.rectangle.y * height) + 'px'; - this.style.width = Math.round(this.rectangle.width * width) + 'px'; - this.style.height = Math.round(this.rectangle.height * height) + 'px'; - }, - - /** - * Calculates the coordinates and size of |this|. - * @param {print_preview.Rect} marginsRectangle A rectangle describing the - * selected margins values in percentages. - * @private - */ - getCoordinates_: function(marginsRectangle) { - var pageLocation = previewArea.pageLocationNormalized; - var totalWidth = previewArea.pdfPlugin_.offsetWidth; - var totalHeight = previewArea.pdfPlugin_.offsetHeight; - var offsetY = (MarginsUIPair.CLICKABLE_REGION / 2) / totalHeight; - var offsetX = (MarginsUIPair.CLICKABLE_REGION / 2) / totalWidth; - - if (this.isTop_()) { - var lineCoordinates = new print_preview.Rect( - pageLocation.x, - marginsRectangle.y - offsetY, - pageLocation.width, - MarginsUIPair.CLICKABLE_REGION / totalHeight); - } else if (this.isBottom_()) { - var lineCoordinates = new print_preview.Rect( - pageLocation.x, - marginsRectangle.bottom - offsetY, - pageLocation.width, - MarginsUIPair.CLICKABLE_REGION / totalHeight); - } else if (this.isRight_()) { - var lineCoordinates = new print_preview.Rect( - marginsRectangle.right - offsetX, - pageLocation.y, - MarginsUIPair.CLICKABLE_REGION / totalWidth, - pageLocation.height); - } else if (this.isLeft_()) { - var lineCoordinates = new print_preview.Rect( - marginsRectangle.x - offsetX, - pageLocation.y, - MarginsUIPair.CLICKABLE_REGION / totalWidth, - pageLocation.height); - } - return lineCoordinates; - }, - - /** - * @return {boolean} True if |this| refers to the top margin. - * @private - */ - isTop_: function() { - return this.marginGroup == print_preview.MarginSettings.TOP_GROUP; - }, - - /** - * @return {boolean} True if |this| refers to the bottom margin. - * @private - */ - isBottom_: function() { - return this.marginGroup == print_preview.MarginSettings.BOTTOM_GROUP; - }, - - /** - * @return {boolean} True if |this| refers to the left margin. - * @private - */ - isLeft_: function() { - return this.marginGroup == print_preview.MarginSettings.LEFT_GROUP; - }, - - /** - * @return {boolean} True if |this| refers to the bottom margin. - * @private - */ - isRight_: function() { - return this.marginGroup == print_preview.MarginSettings.RIGHT_GROUP; - }, - - /** - * Adds event listeners for events related to dragging. - */ - addEventListeners_: function() { - this.onmousedown = this.onMouseDown_.bind(this); - }, - - /** - * Executes whenever a mousedown event occurs on |this| or its child nodes. - * @param {MouseEvent} e The event that occured. - */ - onMouseDown_: function(e) { - if (e.button != 0) - return; - if (e.target != this) - return; - var point = print_preview.MarginsUI.convert({x: e.x, y: e.y}); - this.originBeforeDragging = { x: this.offsetLeft, y: this.offsetTop }; - this.mousePointerOffset = - { x: point.x - this.offsetLeft, y: point.y - this.offsetTop }; - cr.dispatchSimpleEvent(this, customEvents.MARGIN_LINE_MOUSE_DOWN); - }, - - /** - * Executes whenever a mouseup event occurs while |this| is dragged. - */ - onMouseUp: function() { - this.box_.onTextValueMayHaveChanged(); - }, - - /** - * Moves |this| including all its children to |point|. - * @param {{x: number, y: number}} point The point where |this| should be - * moved. - */ - moveTo: function(point) { - if (this.isTop_() || this.isBottom_()) - this.style.top = point.y - this.mousePointerOffset.y + 'px'; - else - this.style.left = point.x - this.mousePointerOffset.x + 'px'; - }, - - /** - * @param {{x: number, y: number}} rhs The point to compare with. - * @return {number} The horizontal or vertical displacement in pixels - * between |this.originBeforeDragging| and |rhs|. Note: Bottom margin - * grows upwards, right margin grows when going to the left. - */ - getDragDisplacementFrom: function(rhs) { - var dragDisplacement = 0; - if (this.isTop_() || this.isBottom_()) { - dragDisplacement = (rhs.y - this.originBeforeDragging.y - - this.mousePointerOffset.y) / previewArea.height; - } else { - dragDisplacement = (rhs.x - this.originBeforeDragging.x - - this.mousePointerOffset.x) / previewArea.width; - } - - if (this.isBottom_() || this.isRight_()) - dragDisplacement *= -1; - return dragDisplacement; - }, - - /** - * Updates |this| while dragging is in progress. Takes care of rejecting - * invalid margin values. - * @param {number} dragDeltaInPoints The difference in points between the - * currently applied margin and the margin indicated by - * |destinationPoint|. - * @param {{x: number, y: number}} destinationPoint The point where the - * margin line should be drawn if |dragDeltaInPoints| is applied. - */ - updateWhileDragging: function(dragDeltaInPoints, destinationPoint) { - this.box_.updateWhileDragging(dragDeltaInPoints); - var validity = this.box_.validateDelta(dragDeltaInPoints); - if (validity == print_preview.marginValidationStates.WITHIN_RANGE) - this.moveTo(destinationPoint); - else if (validity == print_preview.marginValidationStates.TOO_SMALL) - this.snapToMinValue_(); - else if (validity == print_preview.marginValidationStates.TOO_BIG) - this.snapToMaxValue_(); - }, - - /** - * Snaps |this| to the minimum allowed margin value (0). Happens whenever - * the user drags the margin line to a smaller value than the minimum - * allowed. - * @private - */ - snapToMinValue_: function() { - var pageInformation = previewArea.pageLocationNormalized; - var newMarginsRectangle = this.marginsRectangle.clone(); - if (this.isTop_()) { - newMarginsRectangle.y = pageInformation.y; - } else if (this.isLeft_()) { - newMarginsRectangle.x = pageInformation.x; - } else if (this.isRight_()) { - newMarginsRectangle.x = pageInformation.x; - newMarginsRectangle.width = pageInformation.width; - } else if (this.isBottom_()) { - newMarginsRectangle.y = pageInformation.y; - newMarginsRectangle.height = pageInformation.height; - } - - this.rectangle = this.getCoordinates_(newMarginsRectangle); - this.drawDraggableArea_(); - }, - - /** - * Snaps |this| to the maximum allowed margin value. Happens whenever - * the user drags the margin line to a larger value than the maximum - * allowed. - * @private - */ - snapToMaxValue_: function() { - var newMarginsRectangle = this.marginsRectangle.clone(); - - if (this.isTop_()) { - newMarginsRectangle.y = this.valueLimitInPercent; - } else if (this.isLeft_()) { - newMarginsRectangle.x = this.valueLimitInPercent; - } else if (this.isRight_()) { - newMarginsRectangle.x = 0; - newMarginsRectangle.width = this.valueLimitInPercent; - } else if (this.isBottom_()) { - newMarginsRectangle.y = 0; - newMarginsRectangle.height = this.valueLimitInPercent; - } - - this.rectangle = this.getCoordinates_(newMarginsRectangle); - this.drawDraggableArea_(); - } - }; - - return { - MarginsUIPair: MarginsUIPair - }; -}); diff --git a/chrome/browser/resources/print_preview/more_options.html b/chrome/browser/resources/print_preview/more_options.html deleted file mode 100644 index c6032554..0000000 --- a/chrome/browser/resources/print_preview/more_options.html +++ /dev/null @@ -1,17 +0,0 @@ -<div id="more-options" class="two-column visible"> - <h1 i18n-content="optionsLabel"></h1> - <div class="right-column"> - <div id="header-footer-option"> - <label> - <input id="header-footer" type="checkbox" checked> - <span i18n-content="optionHeaderFooter"></span> - </label> - </div> - <div id="fit-to-page-option"> - <label> - <input id="fit-to-page" type="checkbox" checked> - <span i18n-content="optionFitToPage"></span> - </label> - </div> - </div> -</div> diff --git a/chrome/browser/resources/print_preview/more_options.js b/chrome/browser/resources/print_preview/more_options.js deleted file mode 100644 index 68957af..0000000 --- a/chrome/browser/resources/print_preview/more_options.js +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a MoreOptions object. This object encapsulates all - * settings and logic related to the more options section. - * @constructor - */ - function MoreOptions() { - // @type {HTMLDivElement} HTML element representing more options. - this.moreOptions_ = $('more-options'); - - // @type {boolean} True if header footer option is hidden. - this.hideHeaderFooterOption_ = true; - - // @type {boolean} True if fit to page option should be hidden. - this.hideFitToPageOption_ = true; - - this.addEventListeners_(); - } - - cr.addSingletonGetter(MoreOptions); - - MoreOptions.prototype = { - /** - * Adding listeners to more options section. - * @private - */ - addEventListeners_: function() { - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - document.addEventListener( - customEvents.HEADER_FOOTER_VISIBILITY_CHANGED, - this.onHeaderFooterVisibilityChanged_.bind(this)); - document.addEventListener(customEvents.PRINTER_SELECTION_CHANGED, - this.onPrinterSelectionChanged_.bind(this)); - }, - - /** - * Listener executing when a |customEvents.HEADER_FOOTER_VISIBILITY_CHANGED| - * event occurs. - * @param {cr.Event} event The event that triggered this listener. - * @private - */ - onHeaderFooterVisibilityChanged_: function(event) { - this.hideHeaderFooterOption_ = !event.headerFooterApplies; - this.updateVisibility_(); - }, - - /** - * Listener executing when a |customEvents.PRINTER_SEELCTION_CHANGED| event - * occurs. - * @param {cr.Event} event The event that triggered this listener. - * @private - */ - onPrinterSelectionChanged_: function(event) { - if (previewModifiable) - return; - this.hideFitToPageOption_ = event.selectedPrinter == PRINT_TO_PDF; - this.updateVisibility_(); - }, - - /** - * Listener executing when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - if (previewModifiable) - this.hideHeaderFooterOption_ = false; - else - this.hideFitToPageOption_ = false; - }, - - /** - * Hides or shows |this.moreOptions_|. - * @private - */ - updateVisibility_: function() { - if (this.hideFitToPageOption_ && this.hideHeaderFooterOption_) - fadeOutOption(this.moreOptions_); - else - fadeInOption(this.moreOptions_); - } - }; - - return { - MoreOptions: MoreOptions - }; -}); diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js new file mode 100644 index 0000000..8acec69 --- /dev/null +++ b/chrome/browser/resources/print_preview/native_layer.js @@ -0,0 +1,727 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * An interface to the native Chromium printing system layer. + * @constructor + * @extends {cr.EventTarget} + */ + function NativeLayer() { + cr.EventTarget.call(this); + + // Bind global handlers + global['setInitialSettings'] = this.onSetInitialSettings_.bind(this); + global['setUseCloudPrint'] = this.onSetUseCloudPrint_.bind(this); + global['setPrinters'] = this.onSetPrinters_.bind(this); + global['updateWithPrinterCapabilities'] = + this.onUpdateWithPrinterCapabilities_.bind(this); + global['reloadPrintersList'] = this.onReloadPrintersList_.bind(this); + global['printToCloud'] = this.onPrintToCloud_.bind(this); + global['fileSelectionCancelled'] = + this.onFileSelectionCancelled_.bind(this); + global['fileSelectionCompleted'] = + this.onFileSelectionCompleted_.bind(this); + global['printPreviewFailed'] = this.onPrintPreviewFailed_.bind(this); + global['invalidPrinterSettings'] = + this.onInvalidPrinterSettings_.bind(this); + global['onDidGetDefaultPageLayout'] = + this.onDidGetDefaultPageLayout_.bind(this); + global['onDidGetPreviewPageCount'] = + this.onDidGetPreviewPageCount_.bind(this); + global['reloadPreviewPages'] = this.onReloadPreviewPages_.bind(this); + global['onDidPreviewPage'] = this.onDidPreviewPage_.bind(this); + global['updatePrintPreview'] = this.onUpdatePrintPreview_.bind(this); + global['printScalingDisabledForSourcePDF'] = + this.onPrintScalingDisabledForSourcePDF_.bind(this); + }; + + /** + * Event types dispatched from the Chromium native layer. + * @enum {string} + * @const + */ + NativeLayer.EventType = { + CAPABILITIES_SET: 'print_preview.NativeLayer.CAPABILITIES_SET', + CLOUD_PRINT_ENABLE: 'print_preview.NativeLayer.CLOUD_PRINT_ENABLE', + DESTINATIONS_RELOAD: 'print_preview.NativeLayer.DESTINATIONS_RELOAD', + DISABLE_SCALING: 'print_preview.NativeLayer.DISABLE_SCALING', + FILE_SELECTION_CANCEL: 'print_preview.NativeLayer.FILE_SELECTION_CANCEL', + FILE_SELECTION_COMPLETE: + 'print_preview.NativeLayer.FILE_SELECTION_COMPLETE', + INITIAL_SETTINGS_SET: 'print_preview.NativeLayer.INITIAL_SETTINGS_SET', + LOCAL_DESTINATIONS_SET: 'print_preview.NativeLayer.LOCAL_DESTINATIONS_SET', + PAGE_COUNT_READY: 'print_preview.NativeLayer.PAGE_COUNT_READY', + PAGE_LAYOUT_READY: 'print_preview.NativeLayer.PAGE_LAYOUT_READY', + PAGE_PREVIEW_READY: 'print_preview.NativeLayer.PAGE_PREVIEW_READY', + PREVIEW_GENERATION_DONE: + 'print_preview.NativeLayer.PREVIEW_GENERATION_DONE', + PREVIEW_GENERATION_FAIL: + 'print_preview.NativeLayer.PREVIEW_GENERATION_FAIL', + PREVIEW_RELOAD: 'print_preview.NativeLayer.PREVIEW_RELOAD', + PRINT_TO_CLOUD: 'print_preview.NativeLayer.PRINT_TO_CLOUD', + SETTINGS_INVALID: 'print_preview.NativeLayer.SETTINGS_INVALID' + }; + + /** + * Constant values matching printing::DuplexMode enum. + * @enum {number} + */ + NativeLayer.DuplexMode = { + SIMPLEX: 0, + LONG_EDGE: 1, + UNKNOWN_DUPLEX_MODE: -1 + }; + + /** + * Enumeration of color modes used by Chromium. + * @enum {number} + * @private + */ + NativeLayer.ColorMode_ = { + GRAY: 1, + COLOR: 2 + }; + + NativeLayer.prototype = { + __proto__: cr.EventTarget.prototype, + + /** Gets the initial settings to initialize the print preview with. */ + startGetInitialSettings: function() { + chrome.send('getInitialSettings'); + }, + + /** + * Requests the system's local print destinations. A LOCAL_DESTINATIONS_SET + * event will be dispatched in response. + */ + startGetLocalDestinations: function() { + chrome.send('getPrinters'); + }, + + /** + * Requests the destination's printing capabilities. A CAPABILITIES_SET + * event will be dispatched in response. + * @param {string} destinationId ID of the destination. + */ + startGetLocalDestinationCapabilities: function(destinationId) { + chrome.send('getPrinterCapabilities', [destinationId]); + }, + + /** + * Requests that a preview be generated. The following events may be + * dispatched in response: + * - PAGE_COUNT_READY + * - PAGE_LAYOUT_READY + * - PAGE_PREVIEW_READY + * - PREVIEW_GENERATION_DONE + * - PREVIEW_GENERATION_FAIL + * - PREVIEW_RELOAD + * @param {print_preview.Destination} destination Destination to print to. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the + * state of the print ticket. + * @param {number} ID of the preview request. + */ + startGetPreview: function(destination, printTicketStore, requestId) { + assert(printTicketStore.isTicketValidForPreview(), + 'Trying to generate preview when ticket is not valid'); + + var pageRanges = []; + if (requestId > 0 && + !printTicketStore.isDocumentModifiable && + printTicketStore.hasPageRangeCapability()) { + pageRanges = printTicketStore.getPageNumberSet().getPageRanges(); + } + + var ticket = { + 'pageRange': pageRanges, // pageRanges, + 'landscape': printTicketStore.isLandscapeEnabled(), + 'color': printTicketStore.isColorEnabled() ? + NativeLayer.ColorMode_.COLOR : NativeLayer.ColorMode_.GRAY, + 'headerFooterEnabled': printTicketStore.isHeaderFooterEnabled(), + 'marginsType': printTicketStore.getMarginsType(), + 'isFirstRequest': requestId == 0, + 'requestID': requestId, + 'previewModifiable': printTicketStore.isDocumentModifiable, + 'printToPDF': + destination != null && + destination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, + 'printWithCloudPrint': destination != null && !destination.isLocal, + 'deviceName': destination == null ? 'foo' : destination.id, + 'cloudPrintID': destination == null ? 'foo' : destination.id, + 'generateDraftData': printTicketStore.isDocumentModifiable, + 'fitToPageEnabled': printTicketStore.isFitToPageEnabled(), + + // NOTE: Even though the following fields don't directly relate to the + // preview, they still need to be included. + 'duplex': printTicketStore.isDuplexEnabled() ? + NativeLayer.DuplexMode.LONG_EDGE : NativeLayer.DuplexMode.SIMPLEX, + 'copies': printTicketStore.getCopies(), + 'collate': printTicketStore.isCollateEnabled() + }; + + if (printTicketStore.hasMarginsCapability() && + printTicketStore.getMarginsType() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + var customMargins = printTicketStore.getCustomMargins(); + var orientationEnum = + print_preview.ticket_items.CustomMargins.Orientation; + ticket['marginsCustom'] = { + 'marginTop': customMargins.get(orientationEnum.TOP), + 'marginRight': customMargins.get(orientationEnum.RIGHT), + 'marginBottom': customMargins.get(orientationEnum.BOTTOM), + 'marginLeft': customMargins.get(orientationEnum.LEFT) + }; + } + + chrome.send( + 'getPreview', + [JSON.stringify(ticket), -1, printTicketStore.isDocumentModifiable]); + }, + + /** + * Persists the selected destination and print ticket for the next print + * session. + * @param {!print_preview.Destination} destination Destination to save. + * @param {!print_preview.PrintTicketStore} printTicketStore Used for + * generating the serialized print ticket to persist. + */ + startSaveDestinationAndTicket: function(destination, printTicketStore) { + chrome.send('saveLastPrinter', [destination.id, '' /*TODO(rltoscano)*/]); + }, + + /** + * Requests that the document be printed. + * @param {!print_preview.Destination} destination Destination to print to. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the + * state of the print ticket. + * @param {print_preview.CloudPrintInterface} cloudPrintInterface Interface + * to Google Cloud Print. + * @param {boolean=} opt_isOpenPdfInPreview Whether to open the PDF in the + * system's preview application. + */ + startPrint: function(destination, printTicketStore, cloudPrintInterface, + opt_isOpenPdfInPreview) { + assert(printTicketStore.isTicketValid(), + 'Trying to print when ticket is not valid'); + + var ticket = { + 'pageRange': printTicketStore.hasPageRangeCapability() ? + printTicketStore.getPageNumberSet().getPageRanges() : [], + 'landscape': printTicketStore.isLandscapeEnabled(), + 'color': printTicketStore.isColorEnabled() ? + NativeLayer.ColorMode_.COLOR : NativeLayer.ColorMode_.GRAY, + 'headerFooterEnabled': printTicketStore.isHeaderFooterEnabled(), + 'marginsType': printTicketStore.getMarginsType(), + 'generateDraftData': true, // TODO(rltoscano): What should this be? + 'duplex': printTicketStore.isDuplexEnabled() ? + NativeLayer.DuplexMode.LONG_EDGE : NativeLayer.DuplexMode.SIMPLEX, + 'copies': printTicketStore.getCopies(), + 'collate': printTicketStore.isCollateEnabled(), + 'previewModifiable': printTicketStore.isDocumentModifiable, + 'printToPDF': destination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, + 'printWithCloudPrint': !destination.isLocal, + 'deviceName': destination.id, + 'isFirstRequest': false, + 'requestID': -1, + 'fitToPageEnabled': printTicketStore.isFitToPageEnabled() + }; + + if (!destination.isLocal && !destination.isPrintWithCloudPrint) { + // We can't set cloudPrintID if the destination is "Print with Cloud + // Print" because the native system will try to print to Google Cloud + // Print with this ID instead of opening a Google Cloud Print dialog. + ticket['cloudPrintID'] = destination.id; + } + + if (printTicketStore.hasMarginsCapability() && + printTicketStore.getMarginsType() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + var customMargins = printTicketStore.getCustomMargins(); + var orientationEnum = + print_preview.ticket_items.CustomMargins.Orientation; + ticket['marginsCustom'] = { + 'marginTop': customMargins.get(orientationEnum.TOP), + 'marginRight': customMargins.get(orientationEnum.RIGHT), + 'marginBottom': customMargins.get(orientationEnum.BOTTOM), + 'marginLeft': customMargins.get(orientationEnum.LEFT) + }; + } + + if (opt_isOpenPdfInPreview) { + ticket['OpenPDFInPreview'] = true; + } + + var cloudTicket = null; + if (!destination.isLocal) { + assert(cloudPrintInterface != null, + 'Trying to print to a cloud destination but Google Cloud ' + + 'Print integration is disabled'); + cloudTicket = cloudPrintInterface.createPrintTicket( + destination, printTicketStore); + cloudTicket = JSON.stringify(cloudTicket); + } + + chrome.send('print', [JSON.stringify(ticket), cloudTicket]); + }, + + /** Requests that the current pending print request be cancelled. */ + startCancelPendingPrint: function() { + chrome.send('cancelPendingPrintRequest'); + }, + + /** Shows the system's native printing dialog. */ + startShowSystemDialog: function() { + chrome.send('showSystemDialog'); + }, + + /** Closes the print preview dialog. */ + startCloseDialog: function() { + chrome.send('closePrintPreviewTab'); + chrome.send('DialogClose'); + }, + + /** Hide the print preview dialog and allow the native layer to close it. */ + startHideDialog: function() { + chrome.send('hidePreview'); + }, + + /** + * Opens the Google Cloud Print sign-in dialog. The DESTINATIONS_RELOAD + * event will be dispatched in response. + */ + startCloudPrintSignIn: function() { + chrome.send('signIn'); + }, + + /** Navigates the user to the system printer settings interface. */ + startManageLocalPrinters: function() { + chrome.send('manageLocalPrinters'); + }, + + /** Navigates the user to the Google Cloud Print management page. */ + startManageCloudPrinters: function() { + chrome.send('manageCloudPrinters'); + }, + + /** + * @param {object} initialSettings Object containing all initial settings. + */ + onSetInitialSettings_: function(initialSettings) { + // TODO(rltoscano): Use initialSettings['cloudPrintData'] to prepopulate + // destination and initial print ticket. + var numberFormatSymbols = + print_preview.MeasurementSystem.parseNumberFormat( + initialSettings['numberFormat']); + var unitType = print_preview.MeasurementSystem.UnitType.IMPERIAL; + if (initialSettings['measurementSystem'] != null) { + unitType = initialSettings['measurementSystem']; + } + var measurementSystem = new print_preview.MeasurementSystem( + numberFormatSymbols[0], + numberFormatSymbols[1], + unitType); + + var customMargins = null; + if (initialSettings.hasOwnProperty('marginTop') && + initialSettings.hasOwnProperty('marginRight') && + initialSettings.hasOwnProperty('marginBottom') && + initialSettings.hasOwnProperty('marginLeft')) { + customMargins = new print_preview.Margins( + initialSettings['marginTop'] || 0, + initialSettings['marginRight'] || 0, + initialSettings['marginBottom'] || 0, + initialSettings['marginLeft'] || 0); + } + + var marginsType = null; + if (initialSettings.hasOwnProperty('marginsType')) { + marginsType = initialSettings['marginsType']; + } + + var nativeInitialSettings = new print_preview.NativeInitialSettings( + initialSettings['printAutomaticallyInKioskMode'] || false, + numberFormatSymbols[0] || ',', + numberFormatSymbols[1] || '.', + unitType, + initialSettings['previewModifiable'] || false, + marginsType, + customMargins, + initialSettings['duplex'] || false, + initialSettings['headerFooterEnabled'] || false, + initialSettings['printerName'] || null); + + var initialSettingsSetEvent = new cr.Event( + NativeLayer.EventType.INITIAL_SETTINGS_SET); + initialSettingsSetEvent.initialSettings = nativeInitialSettings; + this.dispatchEvent(initialSettingsSetEvent); + }, + + /** + * Turn on the integration of Cloud Print. + * @param {string} cloudPrintURL The URL to use for cloud print servers. + * @private + */ + onSetUseCloudPrint_: function(cloudPrintURL) { + var cloudPrintEnableEvent = new cr.Event( + NativeLayer.EventType.CLOUD_PRINT_ENABLE); + cloudPrintEnableEvent.baseCloudPrintUrl = cloudPrintURL; + this.dispatchEvent(cloudPrintEnableEvent); + }, + + /** + * Updates the print preview with local printers. + * Called from PrintPreviewHandler::SetupPrinterList(). + * @param {Array} printers Array of printer info objects. + * @private + */ + onSetPrinters_: function(printers) { + var localDestsSetEvent = new cr.Event( + NativeLayer.EventType.LOCAL_DESTINATIONS_SET); + localDestsSetEvent.destinationInfos = printers; + this.dispatchEvent(localDestsSetEvent); + }, + + /** + * Called when native layer gets settings information for a requested local + * destination. + * @param {Object} settingsInfo printer setting information. + * @private + */ + onUpdateWithPrinterCapabilities_: function(settingsInfo) { + var capsSetEvent = new cr.Event(NativeLayer.EventType.CAPABILITIES_SET); + capsSetEvent.settingsInfo = settingsInfo; + this.dispatchEvent(capsSetEvent); + }, + + /** Reloads the printer list. */ + onReloadPrintersList_: function() { + cr.dispatchSimpleEvent(this, NativeLayer.EventType.DESTINATIONS_RELOAD); + }, + + /** + * Called from the C++ layer. + * Take the PDF data handed to us and submit it to the cloud, closing the + * print preview tab once the upload is successful. + * @param {string} data Data to send as the print job. + * @private + */ + onPrintToCloud_: function(data) { + var printToCloudEvent = new cr.Event( + NativeLayer.EventType.PRINT_TO_CLOUD); + printToCloudEvent.data = data; + this.dispatchEvent(printToCloudEvent); + }, + + /** + * Called from PrintPreviewUI::OnFileSelectionCancelled to notify the print + * preview tab regarding the file selection cancel event. + * @private + */ + onFileSelectionCancelled_: function() { + cr.dispatchSimpleEvent(this, NativeLayer.EventType.FILE_SELECTION_CANCEL); + }, + + /** + * Called from PrintPreviewUI::OnFileSelectionCompleted to notify the print + * preview tab regarding the file selection completed event. + * @private + */ + onFileSelectionCompleted_: function() { + // If the file selection is completed and the tab is not already closed it + // means that a pending print to pdf request exists. + cr.dispatchSimpleEvent( + this, NativeLayer.EventType.FILE_SELECTION_COMPLETE); + }, + + /** + * Display an error message when print preview fails. + * Called from PrintPreviewMessageHandler::OnPrintPreviewFailed(). + * @private + */ + onPrintPreviewFailed_: function() { + cr.dispatchSimpleEvent( + this, NativeLayer.EventType.PREVIEW_GENERATION_FAIL); + }, + + /** + * Display an error message when encountered invalid printer settings. + * Called from PrintPreviewMessageHandler::OnInvalidPrinterSettings(). + * @private + */ + onInvalidPrinterSettings_: function() { + cr.dispatchSimpleEvent(this, NativeLayer.EventType.SETTINGS_INVALID); + }, + + /** + * @param {{contentWidth: number, contentHeight: number, marginLeft: number, + * marginRight: number, marginTop: number, marginBottom: number, + * printableAreaX: number, printableAreaY: number, + * printableAreaWidth: number, printableAreaHeight: number}} + * pageLayout Specifies default page layout details in points. + * @param {boolean} hasCustomPageSizeStyle Indicates whether the previewed + * document has a custom page size style. + * @private + */ + onDidGetDefaultPageLayout_: function(pageLayout, hasCustomPageSizeStyle) { + var pageLayoutChangeEvent = new cr.Event( + NativeLayer.EventType.PAGE_LAYOUT_READY); + pageLayoutChangeEvent.pageLayout = pageLayout; + pageLayoutChangeEvent.hasCustomPageSizeStyle = hasCustomPageSizeStyle; + this.dispatchEvent(pageLayoutChangeEvent); + }, + + /** + * Update the page count and check the page range. + * Called from PrintPreviewUI::OnDidGetPreviewPageCount(). + * @param {number} pageCount The number of pages. + * @param {number} previewResponseId The preview request id that resulted in + * this response. + * @private + */ + onDidGetPreviewPageCount_: function(pageCount, previewResponseId) { + var pageCountChangeEvent = new cr.Event( + NativeLayer.EventType.PAGE_COUNT_READY); + pageCountChangeEvent.pageCount = pageCount; + pageCountChangeEvent.previewResponseId = previewResponseId; + this.dispatchEvent(pageCountChangeEvent); + }, + + /** + * Called when no pipelining previewed pages. + * @param {string} previewUid Preview unique identifier. + * @param {number} previewResponseId The preview request id that resulted in + * this response. + * @private + */ + onReloadPreviewPages_: function(previewUid, previewResponseId) { + var previewReloadEvent = new cr.Event( + NativeLayer.EventType.PREVIEW_RELOAD); + previewReloadEvent.previewUid = previewUid; + previewReloadEvent.previewResponseId = previewResponseId; + this.dispatchEvent(previewReloadEvent); + }, + + /** + * Notification that a print preview page has been rendered. + * Check if the settings have changed and request a regeneration if needed. + * Called from PrintPreviewUI::OnDidPreviewPage(). + * @param {number} pageNumber The page number, 0-based. + * @param {string} previewUid Preview unique identifier. + * @param {number} previewResponseId The preview request id that resulted in + * this response. + * @private + */ + onDidPreviewPage_: function(pageNumber, previewUid, previewResponseId) { + var pagePreviewGenEvent = new cr.Event( + NativeLayer.EventType.PAGE_PREVIEW_READY); + pagePreviewGenEvent.pageIndex = pageNumber; + pagePreviewGenEvent.previewUid = previewUid; + pagePreviewGenEvent.previewResponseId = previewResponseId; + this.dispatchEvent(pagePreviewGenEvent); + }, + + /** + * Update the print preview when new preview data is available. + * Create the PDF plugin as needed. + * Called from PrintPreviewUI::PreviewDataIsAvailable(). + * @param {string} previewUid Preview unique identifier. + * @param {number} previewResponseId The preview request id that resulted in + * this response. + * @private + */ + onUpdatePrintPreview_: function(previewUid, previewResponseId) { + var previewGenDoneEvent = new cr.Event( + NativeLayer.EventType.PREVIEW_GENERATION_DONE); + previewGenDoneEvent.previewUid = previewUid; + previewGenDoneEvent.previewResponseId = previewResponseId; + this.dispatchEvent(previewGenDoneEvent); + }, + + /** + * Updates the fit to page option state based on the print scaling option of + * source pdf. PDF's have an option to enable/disable print scaling. When we + * find out that the print scaling option is disabled for the source pdf, we + * uncheck the fitToPage_ to page checkbox. This function is called from C++ + * code. + * @private + */ + onPrintScalingDisabledForSourcePDF_: function() { + cr.dispatchSimpleEvent(this, NativeLayer.EventType.DISABLE_SCALING); + } + }; + + /** + * Initial settings retrieved from the native layer. + * @param {boolean} isInKioskAutoPrintMode Whether the print preview should be + * in auto-print mode. + * @param {string} thousandsDelimeter Character delimeter of thousands digits. + * @param {string} decimalDelimeter Character delimeter of the decimal point. + * @param {print_preview.MeasurementSystem.UnitType} unitType Unit type of + * local machine's measurement system. + * @param {boolean} isDocumentModifiable Whether the document to print is + * modifiable. + * @param {?print_preview.ticket_items.MarginsType.Value} marginsType Initial + * margins type. + * @param {print_preview.Margins} customMargins Initial custom margins. + * @param {boolean} isDuplexEnabled Whether duplexing is initially enabled. + * @param {boolean} isHeaderFooterEnabled Whether the header-footer is + * initially enabled. + * @param {?string} initialDestinationId ID of the destination to initially + * select. + * @constructor + */ + function NativeInitialSettings( + isInKioskAutoPrintMode, + thousandsDelimeter, + decimalDelimeter, + unitType, + isDocumentModifiable, + marginsType, + customMargins, + isDuplexEnabled, + isHeaderFooterEnabled, + initialDestinationId) { + + /** + * Whether the print preview should be in auto-print mode. + * @type {boolean} + * @private + */ + this.isInKioskAutoPrintMode_ = isInKioskAutoPrintMode; + + /** + * Character delimeter of thousands digits. + * @type {string} + * @private + */ + this.thousandsDelimeter_ = thousandsDelimeter; + + /** + * Character delimeter of the decimal point. + * @type {string} + * @private + */ + this.decimalDelimeter_ = decimalDelimeter; + + /** + * Unit type of local machine's measurement system. + * @type {string} + * @private + */ + this.unitType_ = unitType; + + /** + * Whether the document to print is modifiable. + * @type {boolean} + * @private + */ + this.isDocumentModifiable_ = isDocumentModifiable; + + /** + * Initial margins type. + * @type {?print_preview.ticket_items.MarginsType.Value} + * @private + */ + this.marginsType_ = marginsType; + + /** + * Initial custom margins. + * @type {print_preview.Margins} + * @private + */ + this.customMargins_ = customMargins; + + /** + * Whether duplexing is initially enabled. + * @type {boolean} + * @private + */ + this.isDuplexEnabled_ = isDuplexEnabled; + + /** + * Whether the header-footer is initially enabled. + * @type {boolean} + * @private + */ + this.isHeaderFooterEnabled_ = isHeaderFooterEnabled; + + /** + * ID of the initially selected destination. + * @type {?string} + * @private + */ + this.initialDestinationId_ = initialDestinationId; + }; + + NativeInitialSettings.prototype = { + /** + * @return {boolean} Whether the print preview should be in auto-print mode. + */ + get isInKioskAutoPrintMode() { + return this.isInKioskAutoPrintMode_; + }, + + /** @return {string} Character delimeter of thousands digits. */ + get thousandsDelimeter() { + return this.thousandsDelimeter_; + }, + + /** @return {string} Character delimeter of the decimal point. */ + get decimalDelimeter() { + return this.decimalDelimeter_; + }, + + /** + * @return {print_preview.MeasurementSystem.UnitType} Unit type of local + * machine's measurement system. + */ + get unitType() { + return this.unitType_; + }, + + /** @return {boolean} Whether the document to print is modifiable. */ + get isDocumentModifiable() { + return this.isDocumentModifiable_; + }, + + /** + * @return {?print_preview.ticket_items.MarginsType.Value} Initial margins + * type or {@code null} if not initially set. + */ + get marginsType() { + return this.marginsType_; + }, + + /** @return {print_preview.Margins} Initial custom margins. */ + get customMargins() { + return this.customMargins_; + }, + + /** @return {boolean} Whether duplexing is initially enabled. */ + get isDuplexEnabled() { + return this.isDuplexEnabled_; + }, + + /** @return {boolean} Whether the header-footer is initially enabled. */ + get isHeaderFooterEnabled() { + return this.isHeaderFooterEnabled_; + }, + + /** @return {?string} ID of the initially selected destination. */ + get initialDestinationId() { + return this.initialDestinationId_; + } + }; + + // Export + return { + NativeInitialSettings: NativeInitialSettings, + NativeLayer: NativeLayer + }; +}); diff --git a/chrome/browser/resources/print_preview/page_settings.html b/chrome/browser/resources/print_preview/page_settings.html deleted file mode 100644 index d4e55d2..0000000 --- a/chrome/browser/resources/print_preview/page_settings.html +++ /dev/null @@ -1,21 +0,0 @@ -<div class="two-column visible"> - <h1 i18n-content="pagesLabel"></h1> - <div class="right-column"> - <div class="radio"><label> - <input id="all-pages" name="pages" checked type="radio"> - <span i18n-content="optionAllPages"></span> - </label></div> - <div> - <div id="print-pages-div"> - <input id="print-pages" name="pages" type="radio" - i18n-values="aria-label:printPagesLabel;"> - <input id="individual-pages" type="text" - i18n-values="placeholder:examplePageRangeText"> - </div> - <span id="individual-pages-hint" class="hint" - i18n-content="pageRangeInstruction" aria-hidden="true" - aria-live="polite"> - </span> - </div> - </div> -</div> diff --git a/chrome/browser/resources/print_preview/page_settings.js b/chrome/browser/resources/print_preview/page_settings.js deleted file mode 100644 index b03306c..0000000 --- a/chrome/browser/resources/print_preview/page_settings.js +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a PageSettings object. This object encapsulates all settings and - * logic related to page selection. - * @constructor - */ - function PageSettings() { - this.allPagesRadioButton_ = $('all-pages'); - this.selectedPagesRadioButton_ = $('print-pages'); - this.selectedPagesTextfield_ = $('individual-pages'); - this.selectedPagesHint_ = $('individual-pages-hint'); - - // Timer id of |this.selectedPagesTextfield|. It is used to reset the timer - // whenever needed. - this.timerId_; - - // Contains the previously selected pages (pages requested by last - // preview request). It is used in - // |this.onSelectedPagesMayHaveChanged_()| to make sure that a new preview - // is not requested more often than necessary. - this.previouslySelectedPages_ = []; - - // The total page count of the previewed document regardless of which pages - // the user has selected. - this.totalPageCount_ = undefined; - this.addEventListeners_(); - } - - cr.addSingletonGetter(PageSettings); - - PageSettings.prototype = { - /** - * The text that is currently in |this.selectedPagesTextfield|. - * @type {string} - */ - get selectedPagesText() { - return this.selectedPagesTextfield_.value; - }, - - /** - * The radio button corresponding to "all pages selected". - * @type {HTMLInputElement} - */ - get allPagesRadioButton() { - return this.allPagesRadioButton_; - }, - - /** - * The radio button corresponding to "specific pages selected". - * @type {HTMLInputElement} - */ - get selectedPagesRadioButton() { - return this.selectedPagesRadioButton_; - }, - - /** - * The textfield containing page ranges as specified by the user. - * @type {HTMLInputElement} - */ - get selectedPagesTextfield() { - return this.selectedPagesTextfield_; - }, - - /** - * The span element containing the hint shown to the user when page - * selection is not valid. - * @type {HTMLElement} - */ - get selectedPagesHint() { - return this.selectedPagesHint_; - }, - - /** - * The total page count of the previewed document regardless of which pages - * the user has selected. If the total count is not known this value must - * be undefined. - * @type {number} - */ - get totalPageCount() { - return this.totalPageCount_; - }, - - /** - * @param {number} count The number to assing to |this.totalPageCount_|. - */ - set totalPageCount(count) { - this.totalPageCount_ = count; - }, - - /** - * Returns the selected pages in ascending order without any duplicates. - * - * @return {Array.<number>} The selected pages. - */ - get selectedPagesSet() { - var selectedPagesText = this.selectedPagesText; - - if (this.allPagesRadioButton.checked || selectedPagesText.length == 0) - selectedPagesText = '1-' + this.totalPageCount_; - - var pageList = pageRangeTextToPageList(selectedPagesText, - this.totalPageCount_); - return pageListToPageSet(pageList); - }, - - /** - * Returns the previously selected pages in ascending order without any - * duplicates. - * - * @return {Array.<number>} The previously selected pages. - */ - get previouslySelectedPages() { - return this.previouslySelectedPages_; - }, - - /** - * Returns an array of objects describing the selected page ranges. See - * documentation of pageSetToPageRanges() for more details. - * @return {Array.<{from: number, to: number}>} An array of page range - * objects. - */ - get selectedPageRanges() { - return pageSetToPageRanges(this.selectedPagesSet); - }, - - /** - * Invalidates |this.totalPageCount_| to indicate that the total number of - * pages is not known. - * @private - */ - invalidateTotalPageCount_: function() { - this.totalPageCount_ = undefined; - }, - - /** - * Invlidates |this.previouslySelectedPages_| to indicate that this value - * does no longer apply. - * @private - */ - invalidatePreviouslySelectedPages_: function() { - this.previouslySelectedPages_.length = 0; - }, - - /** - * Resets all the state variables of this object and hides - * |this.selectedPagesHint|. - */ - resetState: function() { - this.selectedPagesTextfield.classList.remove('invalid'); - fadeOutElement(this.selectedPagesHint_); - this.invalidateTotalPageCount_(); - this.invalidatePreviouslySelectedPages_(); - }, - - /** - * Updates |this.totalPageCount_| and |this.previouslySelectedPages_|, - * only if they have been previously invalidated. - * @param {number} newTotalPageCount The new total page count. - */ - updateState: function(newTotalPageCount) { - if (!this.totalPageCount_) - this.totalPageCount_ = newTotalPageCount; - - if (this.previouslySelectedPages_.length == 0) { - for (var i = 0; i < this.totalPageCount_; i++) - this.previouslySelectedPages_.push(i + 1); - } - - if (!this.isPageSelectionValid()) - this.onSelectedPagesTextfieldChanged(); - }, - - /** - * Updates |this.previouslySelectedPages_| with the currently selected - * pages. - */ - updatePageSelection: function() { - this.previouslySelectedPages_ = this.selectedPagesSet; - }, - - /** - * @private - * @return {boolean} true if currently selected pages differ from - * |this.previouslySelectesPages_|. - */ - hasPageSelectionChanged_: function() { - return !areArraysEqual(this.previouslySelectedPages_, - this.selectedPagesSet); - }, - - /** - * Checks if the page selection has changed and is valid. - * @return {boolean} true if the page selection is changed and is valid. - */ - hasPageSelectionChangedAndIsValid: function() { - return this.isPageSelectionValid() && this.hasPageSelectionChanged_(); - }, - - /** - * Validates the contents of |this.selectedPagesTextfield|. - * - * @return {boolean} true if the text is valid. - */ - isPageSelectionValid: function() { - if (this.allPagesRadioButton_.checked || - this.selectedPagesText.length == 0) { - return true; - } - return isPageRangeTextValid(this.selectedPagesText, this.totalPageCount_); - }, - - /** - * Checks all page selection related settings and requests a new print - * previw if needed. - * @return {boolean} true if a new preview was requested. - */ - requestPrintPreviewIfNeeded: function() { - if (this.hasPageSelectionChangedAndIsValid()) { - this.updatePageSelection(); - requestPrintPreview(); - return true; - } - if (!this.isPageSelectionValid()) - this.onSelectedPagesTextfieldChanged(); - return false; - }, - - /** - * Validates the selected pages and updates the hint accordingly. - * @private - */ - validateSelectedPages_: function() { - if (this.isPageSelectionValid()) { - this.selectedPagesTextfield.classList.remove('invalid'); - fadeOutElement(this.selectedPagesHint_); - this.selectedPagesHint.setAttribute('aria-hidden', 'true'); - } else { - this.selectedPagesTextfield.classList.add('invalid'); - this.selectedPagesHint.classList.remove('suggestion'); - this.selectedPagesHint.setAttribute('aria-hidden', 'false'); - this.selectedPagesHint.innerHTML = - localStrings.getStringF('pageRangeInstruction', - localStrings.getString( - 'examplePageRangeText')); - fadeInElement(this.selectedPagesHint); - } - }, - - /** - * Executes whenever a blur event occurs on |this.selectedPagesTextfield| - * or when the timer expires. It takes care of - * 1) showing/hiding warnings/suggestions - * 2) updating print button/summary - */ - onSelectedPagesTextfieldChanged: function() { - this.validateSelectedPages_(); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - }, - - /** - * When the user stops typing in |this.selectedPagesTextfield| or clicks on - * |allPagesRadioButton|, a new print preview is requested, only if - * 1) The input is compeletely valid (it can be parsed in its entirety). - * 2) The newly selected pages differ from |this.previouslySelectedPages_|. - * @private - */ - onSelectedPagesMayHaveChanged_: function() { - if (this.selectedPagesRadioButton_.checked) - this.onSelectedPagesTextfieldChanged(); - - // Toggling between "all pages"/"some pages" radio buttons while having an - // invalid entry in the page selection textfield still requires updating - // the print summary and print button. - if (!this.isPageSelectionValid() || !this.hasPageSelectionChanged_()) { - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - return; - } - requestPrintPreview(); - }, - - /** - * Whenever |this.selectedPagesTextfield| gains focus we add a timer to - * detect when the user stops typing in order to update the print preview. - * @private - */ - addTimerToSelectedPagesTextfield_: function() { - this.timerId_ = window.setTimeout( - this.onSelectedPagesMayHaveChanged_.bind(this), 1000); - }, - - /** - * As the user types in |this.selectedPagesTextfield|, we need to reset - * this timer, since the page ranges are still being edited. - * @private - */ - resetSelectedPagesTextfieldTimer_: function() { - clearTimeout(this.timerId_); - this.addTimerToSelectedPagesTextfield_(); - }, - - /** - * Handles the blur event of |this.selectedPagesTextfield|. Un-checks - * |this.selectedPagesRadioButton| if the input field is empty. - * @private - */ - onSelectedPagesTextfieldBlur_: function() { - clearTimeout(this.timerId_); - if (!this.selectedPagesText.length) { - this.allPagesRadioButton_.checked = true; - this.validateSelectedPages_(); - } - this.onSelectedPagesMayHaveChanged_(); - }, - - /** - * Gives focus to |this.selectedPagesTextfield| when - * |this.selectedPagesRadioButton| is clicked. - * @private - */ - onSelectedPagesRadioButtonChecked_: function() { - this.selectedPagesTextfield_.focus(); - }, - - /** - * Listener executing when an input event occurs in - * |this.selectedPagesTextfield|. Ensures that - * |this.selectedPagesTextfield| is non-empty before checking - * |this.selectedPagesRadioButton|. - * @private - */ - onSelectedPagesTextfieldInput_: function() { - if (this.selectedPagesText.length) - this.selectedPagesRadioButton.checked = true; - this.resetSelectedPagesTextfieldTimer_(); - }, - - /** - * Listener executing whenever a keyup events occurs in the pages textfield. - * @param {!KeyboardEvent} e The event that triggered this listener. - * @private - */ - onKeyUp_: function(e) { - if (e.keyIdentifier == 'Enter') - printHeader.onPrintRequested(); - }, - - /** - * Adding listeners to all pages related controls. The listeners take care - * of altering their behavior depending on |hasPendingPreviewRequest|. - * @private - */ - addEventListeners_: function() { - this.allPagesRadioButton.onclick = - this.onSelectedPagesMayHaveChanged_.bind(this); - this.selectedPagesRadioButton.onclick = - this.onSelectedPagesMayHaveChanged_.bind(this); - this.selectedPagesTextfield.oninput = - this.onSelectedPagesTextfieldInput_.bind(this); - this.selectedPagesTextfield.onfocus = - this.addTimerToSelectedPagesTextfield_.bind(this); - this.selectedPagesTextfield.onblur = - this.onSelectedPagesTextfieldBlur_.bind(this); - this.selectedPagesTextfield.onkeyup = this.onKeyUp_.bind(this); - } - }; - - return { - PageSettings: PageSettings - }; -}); diff --git a/chrome/browser/resources/print_preview/preview_area.js b/chrome/browser/resources/print_preview/preview_area.js deleted file mode 100644 index ed5e92c..0000000 --- a/chrome/browser/resources/print_preview/preview_area.js +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('print_preview', function() { - 'use strict'; - - /** - * Creates a PreviewArea object. It represents the area where the preview - * document is displayed. - * @constructor - */ - function PreviewArea() { - // The embedded pdf plugin object. - this.pdfPlugin_ = null; - - // @type {HTMLDivElement} A layer on top of |this.pdfPlugin_| used for - // displaying messages to the user. - this.overlayLayer = $('overlay-layer'); - // @type {HTMLDivElement} Contains text displayed to the user followed by - // three animated dots. - this.customMessageWithDots_ = $('custom-message-with-dots'); - // @type {HTMLDivElement} Contains text displayed to the user. - this.customMessage_ = $('custom-message'); - // @type {HTMLInputElement} Button associated with a displayed error - // message. - this.errorButton = $('error-button'); - // @type {HTMLDivElement} Contains three animated (dancing) dots. - this.dancingDotsText = $('dancing-dots-text'); - - // True if the pdf document is loaded in the preview area. - this.pdfLoaded_ = false; - - // Contains the zoom level just before a new preview is requested so the - // same zoom level can be restored. - this.zoomLevel_ = null; - // @type {{x: number, y: number}} Contains the page offset values just - // before a new preview is requested so that the scroll amount can be - // restored later. - this.pageOffset_ = null; - // @type {print_preview.Rect} A rectangle describing the postion of the - // most visible page normalized with respect to the total height and width - // of the plugin. - this.pageLocationNormalized = null; - - // @type {EventTracker} Used to keep track of certain event listeners. - this.eventTracker = new EventTracker(); - - this.addEventListeners_(); - } - - cr.addSingletonGetter(PreviewArea); - - PreviewArea.prototype = { - /** - * The width of the plugin area in pixels, excluding any visible scrollbars, - * @type {number} - */ - get width() { - return this.widthPercent * this.pdfPlugin_.offsetWidth; - }, - - /** - * The height of the plugin area in pixels, excluding any visible - * scrollbars. - * @type {number} - */ - get height() { - return this.heightPercent * this.pdfPlugin_.offsetHeight; - }, - - /** - * The width of the plugin area in percent, excluding any visible - * scrollbars. - * @type {number} - */ - get widthPercent() { - var width = this.pdfPlugin_.getWidth(); - var scrollbarWidth = this.pdfPlugin_.getVerticalScrollbarThickness(); - return (width - scrollbarWidth) / width; - }, - - /** - * The height of the plugin area in percent, excluding any visible - * scrollbars. - * @type {number} - */ - get heightPercent() { - var height = this.pdfPlugin_.getHeight(); - var scrollbarHeight = this.pdfPlugin_.getHorizontalScrollbarThickness(); - return (height - scrollbarHeight) / height; - }, - - get pdfPlugin() { - return this.pdfPlugin_; - }, - - get pdfLoaded() { - return this.pdfLoaded_; - }, - - set pdfLoaded(pdfLoaded) { - this.pdfLoaded_ = pdfLoaded; - }, - - /** - * Initializes the PDF plugin and places it on the page. - * @param {!string} srcURL The URL of the document to be loaded in the - * plugin. - * @private - */ - createPDFPlugin_: function(srcURL) { - this.pdfPlugin_ = document.createElement('embed'); - this.pdfPlugin_.setAttribute('id', 'pdf-viewer'); - this.pdfPlugin_.setAttribute( - 'type', 'application/x-google-chrome-print-preview-pdf'); - this.pdfPlugin_.setAttribute('src', srcURL); - this.pdfPlugin_.setAttribute('aria-live', 'polite'); - this.pdfPlugin_.setAttribute('aria-atomic', 'true'); - $('mainview').appendChild(this.pdfPlugin_); - - this.pdfPlugin_.onload('onPDFLoad()'); - this.pdfPlugin_.onScroll('onPreviewPositionChanged()'); - this.pdfPlugin_.onPluginSizeChanged('onPreviewPositionChanged()'); - this.pdfPlugin_.removePrintButton(); - this.pdfPlugin_.grayscale(true); - }, - - /** - * Reloads the plugin with a new url. - * @param {string} srcURL The URL to load in the plugin. - * @private - */ - reloadPDFPlugin_: function(srcURL) { - // Need to call this before the reload(), where the plugin resets its - // internal page count. - this.pdfPlugin_.goToPage('0'); - this.pdfPlugin_.resetPrintPreviewUrl(srcURL); - this.pdfPlugin_.reload(); - this.pdfPlugin_.grayscale( - colorSettings.colorMode == print_preview.ColorSettings.GRAY); - }, - - /** - * Creates the PDF plugin or reloads the existing one. - * @param {number} srcDataIndex Preview data source index. - */ - createOrReloadPDFPlugin: function(srcDataIndex) { - var srcURL = getPageSrcURL(currentPreviewUid, srcDataIndex); - this.pdfPlugin_ ? this.reloadPDFPlugin_(srcURL) : - this.createPDFPlugin_(srcURL); - }, - - /** - * Queries the plugin for the location of the most visible page and updates - * |this.pageLocationNormalized|. - */ - update: function() { - if (!this.pdfLoaded_) - return; - var pluginLocation = - this.pdfPlugin_.getPageLocationNormalized().split(';'); - this.pageLocationNormalized = new print_preview.Rect( - parseFloat(pluginLocation[0]), - parseFloat(pluginLocation[1]), - parseFloat(pluginLocation[2]), - parseFloat(pluginLocation[3])); - }, - - /** - * Resets the state variables of |this|. - */ - resetState: function() { - if (this.pdfPlugin_ && previewModifiable) { - this.zoomLevel_ = this.pdfPlugin_.getZoomLevel(); - this.pageOffset_ = { - x: this.pdfPlugin_.pageXOffset(), - y: this.pdfPlugin_.pageYOffset() - }; - } - this.pdfLoaded_ = false; - }, - - /** - * Adds event listeners for various events. - * @private - */ - addEventListeners_: function() { - document.addEventListener(customEvents.PDF_LOADED, - this.onPDFLoaded_.bind(this)); - }, - - /** - * Listener executing when a |customEvents.PDF_LOADED| event occurs. - * @private - */ - onPDFLoaded_: function() { - this.pdfPlugin_ = $('pdf-viewer'); - this.pdfLoaded_ = true; - if (this.zoomLevel_ != null && this.pageOffset_ != null) { - this.pdfPlugin_.setZoomLevel(this.zoomLevel_); - this.pdfPlugin_.setPageXOffset(this.pageOffset_.x); - this.pdfPlugin_.setPageYOffset(this.pageOffset_.y); - } else { - this.pdfPlugin_.fitToHeight(); - } - }, - - /** - * Hides the |this.overlayLayer| and any messages currently displayed. - */ - hideOverlayLayer: function() { - this.eventTracker.add(this.overlayLayer, 'webkitTransitionEnd', - this.hideOverlayLayerCleanup_.bind(this), false); - if (this.pdfPlugin_) - this.pdfPlugin_.classList.remove('invisible'); - this.overlayLayer.classList.add('invisible'); - }, - - /** - * Displays the "Preview loading..." animation. - */ - showLoadingAnimation: function() { - this.showCustomMessage(localStrings.getString('loading')); - }, - - /** - * Necessary cleanup so that the dancing dots animation is not being - * rendered in the background when not displayed. - */ - hideOverlayLayerCleanup_: function() { - this.customMessageWithDots_.hidden = true; - this.eventTracker.remove(this.overlayLayer, 'webkitTransitionEnd'); - }, - - /** - * Displays |message| followed by three dancing dots animation. - * @param {string} message The message to be displayed. - */ - showCustomMessage: function(message) { - this.customMessageWithDots_.innerHTML = message + - this.dancingDotsText.innerHTML; - this.customMessageWithDots_.hidden = false; - if (this.pdfPlugin_) - this.pdfPlugin_.classList.add('invisible'); - this.overlayLayer.classList.remove('invisible'); - }, - - /** - * Clears the custom message with dots animation. - */ - clearCustomMessageWithDots: function() { - this.customMessageWithDots_.innerHTML = ''; - this.customMessageWithDots_.hidden = true; - }, - - /** - * Display an error message in the center of the preview area. - * @param {string} errorMessage The error message to be displayed. - */ - displayErrorMessageAndNotify: function(errorMessage) { - this.overlayLayer.classList.remove('invisible'); - this.customMessage_.textContent = errorMessage; - this.customMessage_.hidden = false; - this.customMessageWithDots_.innerHTML = ''; - this.customMessageWithDots_.hidden = true; - if (this.pdfPlugin_) { - $('mainview').removeChild(this.pdfPlugin_); - this.pdfPlugin_ = null; - } - cr.dispatchSimpleEvent(document, customEvents.PDF_GENERATION_ERROR); - }, - - /** - * Display an error message in the center of the preview area followed by a - * button. - * @param {string} errorMessage The error message to be displayed. - * @param {string} buttonText The text to be displayed within the button. - * @param {string} buttonListener The listener to be executed when the - * button is clicked. - */ - displayErrorMessageWithButtonAndNotify: function( - errorMessage, buttonText, buttonListener) { - this.errorButton.disabled = false; - this.errorButton.textContent = buttonText; - this.errorButton.onclick = buttonListener; - this.errorButton.hidden = false; - $('system-dialog-throbber').hidden = true; - $('native-print-dialog-throbber').hidden = true; - if (cr.isMac) - $('open-preview-app-throbber').hidden = true; - this.displayErrorMessageAndNotify(errorMessage); - } - }; - - return { - PreviewArea: PreviewArea - }; -}); diff --git a/chrome/browser/resources/print_preview/preview_generator.js b/chrome/browser/resources/print_preview/preview_generator.js new file mode 100644 index 0000000..4787dc4 --- /dev/null +++ b/chrome/browser/resources/print_preview/preview_generator.js @@ -0,0 +1,392 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Interface to the Chromium print preview generator. + * @param {!print_preview.DestinationStore} destinationStore Used to get the + * currently selected destination. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read the + * state of the ticket and write document information. + * @param {!print_preview.NativeLayer} nativeLayer Used to communicate to + * Chromium's preview rendering system. + * @constructor + * @extends {cr.EventTarget} + */ + function PreviewGenerator(destinationStore, printTicketStore, nativeLayer) { + cr.EventTarget.call(this); + + /** + * Used to get the currently selected destination. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + + /** + * Used to read the state of the ticket and write document information. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Interface to the Chromium native layer. + * @type {!print_preview.NativeLayer} + * @private + */ + this.nativeLayer_ = nativeLayer; + + /** + * ID of current in-flight request. Requests that do not share this ID will + * be ignored. + * @type {number} + * @private + */ + this.inFlightRequestId_ = -1; + + /** + * Whether the previews are being generated in landscape mode. + * @type {boolean} + * @private + */ + this.isLandscapeEnabled_ = false; + + /** + * Whether the previews are being generated with a header and footer. + * @type {boolean} + * @private + */ + this.isHeaderFooterEnabled_ = false; + + /** + * Whether the previews are being generated in color. + * @type {boolean} + * @private + */ + this.isColorEnabled_ = false; + + /** + * Whether the document should be fitted to the page. + * @type {boolean} + * @private + */ + this.isFitToPageEnabled_ = false; + + /** + * Page number set used to generate the last preview. + * @type {print_preview.PageNumberSet} + * @private + */ + this.pageNumberSet_ = null; + + /** + * Margins type used to generate the last preview. + * @type {print_preview.ticket_items.MarginsType.Value} + * @private + */ + this.marginsType_ = print_preview.ticket_items.MarginsType.Value.DEFAULT; + + /** + * Destination that was selected for the last preview. + * @type {print_preview.Destination} + * @private + */ + this.selectedDestination_ = null; + + /** + * Event tracker used to keep track of native layer events. + * @type {!EventTracker} + * @private + */ + this.tracker_ = new EventTracker(); + + this.addEventListeners_(); + }; + + /** + * Event types dispatched by the preview generator. + * @enum {string} + */ + PreviewGenerator.EventType = { + // Dispatched when the document can be printed. + DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY', + + // Dispatched when a page preview is ready. The previewIndex field of the + // event is the index of the page in the modified document, not the + // original. So page 4 of the original document might be previewIndex = 0 of + // the modified document. + PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY', + + // Dispatched when the document preview starts to be generated. + PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START', + + // Dispatched when the current print preview request fails. + FAIL: 'print_preview.PreviewGenerator.FAIL' + }; + + PreviewGenerator.prototype = { + __proto__: cr.EventTarget.prototype, + + /** + * Request that new preview be generated. A preview request will not be + * generated if the print ticket has not changed sufficiently. + * @return {boolean} Whether a new preview was actually requested. + */ + requestPreview: function() { + if (!this.printTicketStore_.isTicketValidForPreview()) { + return false; + } + if (!this.hasPreviewChanged_()) { + // Changes to these ticket items might not trigger a new preview, but + // they still need to be recorded. + this.marginsType_ = this.printTicketStore_.getMarginsType(); + return false; + } + this.isLandscapeEnabled_ = this.printTicketStore_.isLandscapeEnabled(); + this.isHeaderFooterEnabled_ = + this.printTicketStore_.isHeaderFooterEnabled(); + this.isColorEnabled_ = this.printTicketStore_.isColorEnabled(); + this.isFitToPageEnabled_ = this.printTicketStore_.isFitToPageEnabled(); + this.pageNumberSet_ = this.printTicketStore_.getPageNumberSet(); + this.marginsType_ = this.printTicketStore_.getMarginsType(); + this.selectedDestination_ = this.destinationStore_.selectedDestination; + + this.inFlightRequestId_++; + this.nativeLayer_.startGetPreview( + this.destinationStore_.selectedDestination, + this.printTicketStore_, + this.inFlightRequestId_); + return true; + }, + + /** Removes all event listeners that the preview generator has attached. */ + removeEventListeners: function() { + this.tracker_.removeAll(); + }, + + /** + * Adds event listeners to the relevant native layer events. + * @private + */ + addEventListeners_: function() { + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PAGE_LAYOUT_READY, + this.onPageLayoutReady_.bind(this)); + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PAGE_COUNT_READY, + this.onPageCountReady_.bind(this)); + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PREVIEW_RELOAD, + this.onPreviewReload_.bind(this)); + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY, + this.onPagePreviewReady_.bind(this)); + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE, + this.onPreviewGenerationDone_.bind(this)); + this.tracker_.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL, + this.onPreviewGenerationFail_.bind(this)); + }, + + /** + * Dispatches a PAGE_READY event to signal that a page preview is ready. + * @param {number} previewIndex Index of the page with respect to the pages + * shown in the preview. E.g an index of 0 is the first displayed page, + * but not necessarily the first original document page. + * @param {number} pageNumber Number of the page with respect to the + * document. A value of 3 means it's the third page of the original + * document. + * @param {string} previewUid Unique identifier of the preview. + * @private + */ + dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) { + var pageGenEvent = new cr.Event(PreviewGenerator.EventType.PAGE_READY); + pageGenEvent.previewIndex = previewIndex; + pageGenEvent.previewUrl = + 'chrome://print/' + previewUid + '/' + (pageNumber - 1) + + '/print.pdf'; + this.dispatchEvent(pageGenEvent); + }, + + /** + * Dispatches a PREVIEW_START event. Signals that the preview should be + * reloaded. + * @param {string} previewUid Unique identifier of the preview. + * @private + */ + dispatchPreviewStartEvent_: function(previewUid) { + var previewStartEvent = new cr.Event( + PreviewGenerator.EventType.PREVIEW_START); + var index = -1; + if (this.printTicketStore_.isDocumentModifiable) { + index = 0; + } + previewStartEvent.previewUrl = + 'chrome://print/' + previewUid + '/' + index + '/print.pdf'; + this.dispatchEvent(previewStartEvent); + }, + + /** + * @return {boolean} Whether the print ticket has changed sufficiently to + * determine whether a new preview request should be issued. + * @private + */ + hasPreviewChanged_: function() { + var ticketStore = this.printTicketStore_; + return this.inFlightRequestId_ == -1 || + ticketStore.isLandscapeEnabled() != this.isLandscapeEnabled_ || + ticketStore.isHeaderFooterEnabled() != this.isHeaderFooterEnabled_ || + ticketStore.isColorEnabled() != this.isColorEnabled_ || + ticketStore.isFitToPageEnabled() != this.isFitToPageEnabled_ || + !ticketStore.getPageNumberSet().equals(this.pageNumberSet_) || + (ticketStore.getMarginsType() != this.marginsType_ && + ticketStore.getMarginsType() != + print_preview.ticket_items.MarginsType.Value.CUSTOM) || + (ticketStore.getMarginsType() == + print_preview.ticket_items.MarginsType.Value.CUSTOM && + !ticketStore.getCustomMargins().equals( + ticketStore.getDocumentMargins())) || + (this.selectedDestination_ != + this.destinationStore_.selectedDestination && + (this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF || + this.selectedDestination_.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF)); + }, + + /** + * Called when the page layout of the document is ready. Always occurs + * as a result of a preview request. + * @param {cr.Event} event Contains layout info about the document. + * @private + */ + onPageLayoutReady_: function(event) { + // NOTE: A request ID is not specified, so assuming its for the current + // in-flight request. + + var origin = new print_preview.Coordinate2d( + event.pageLayout.printableAreaX, + event.pageLayout.printableAreaY); + var size = new print_preview.Size( + event.pageLayout.printableAreaWidth, + event.pageLayout.printableAreaHeight); + + var margins = new print_preview.Margins( + Math.round(event.pageLayout.marginTop), + Math.round(event.pageLayout.marginRight), + Math.round(event.pageLayout.marginBottom), + Math.round(event.pageLayout.marginLeft)); + + var o = print_preview.ticket_items.CustomMargins.Orientation; + var pageSize = new print_preview.Size( + event.pageLayout.contentWidth + + margins.get(o.LEFT) + margins.get(o.RIGHT), + event.pageLayout.contentHeight + + margins.get(o.TOP) + margins.get(o.BOTTOM)); + + this.printTicketStore_.updateDocumentPageInfo( + new print_preview.PrintableArea(origin, size), + pageSize, + event.hasCustomPageSizeStyle, + margins); + }, + + /** + * Called when the document page count is received from the native layer. + * Always occurs as a result of a preview request. + * @param {cr.Event} event Contains the document's page count. + * @private + */ + onPageCountReady_: function(event) { + if (this.inFlightRequestId_ != event.previewResponseId) { + return; // Ignore old response. + } + this.printTicketStore_.updatePageCount(event.pageCount); + this.pageNumberSet_ = this.printTicketStore_.getPageNumberSet(); + }, + + /** + * Called when the print preview should be reloaded. + * @param {cr.Event} event Contains the preview UID and request ID. + * @private + */ + onPreviewReload_: function(event) { + if (this.inFlightRequestId_ != event.previewResponseId) { + return; // Ignore old response. + } + this.dispatchPreviewStartEvent_(event.previewUid); + var pageNumberSet = this.printTicketStore_.getPageNumberSet(); + for (var i = 0; i < pageNumberSet.size; i++) { + var pageNumber = pageNumberSet.getPageNumberAt(i); + this.dispatchPageReadyEvent_(i, pageNumber, event.previewUid); + } + cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY); + }, + + /** + * Called when a page's preview has been generated. Dispatches a + * PAGE_READY event. + * @param {cr.Event} event Contains the page index and preview UID. + * @private + */ + onPagePreviewReady_: function(event) { + if (this.inFlightRequestId_ != event.previewResponseId) { + return; // Ignore old response. + } + var pageNumber = event.pageIndex + 1; + if (this.printTicketStore_.getPageNumberSet().hasPageNumber(pageNumber)) { + var previewIndex = this.printTicketStore_.getPageNumberSet() + .getPageNumberIndex(pageNumber); + if (previewIndex == 0) { + this.dispatchPreviewStartEvent_(event.previewUid); + } + this.dispatchPageReadyEvent_( + previewIndex, pageNumber, event.previewUid); + } + }, + + /** + * Called when the preview generation is complete. Dispatches a + * DOCUMENT_READY event. + * @param {cr.Event} event Contains the preview UID and response ID. + * @private + */ + onPreviewGenerationDone_: function(event) { + if (this.inFlightRequestId_ != event.previewResponseId) { + return; // Ignore old response. + } + // Dispatch a PREVIEW_START event since non-modifiable documents don't + // trigger PAGE_READY events. + if (!this.printTicketStore_.isDocumentModifiable) { + this.dispatchPreviewStartEvent_(event.previewUid); + } + cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY); + }, + + /** + * Called when the preview generation fails. + * @private + */ + onPreviewGenerationFail_: function() { + // NOTE: No request ID is returned from Chromium so its assumed its the + // current one. + cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL); + } + }; + + // Export + return { + PreviewGenerator: PreviewGenerator + }; +}); diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control.css b/chrome/browser/resources/print_preview/previewarea/margin_control.css new file mode 100644 index 0000000..38c5834 --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/margin_control.css @@ -0,0 +1,91 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#preview-area .margin-control { + -webkit-transition: opacity 150ms linear; + position: absolute; +} + +#preview-area .margin-control.invisible { + opacity: 0; + pointer-events: none; +} + +#preview-area .margin-control.margin-control-top, +#preview-area .margin-control.margin-control-bottom { + cursor: ns-resize; + padding: 8px 0; + width: 100%; +} + +#preview-area .margin-control.margin-control-left, +#preview-area .margin-control.margin-control-right { + cursor: ew-resize; + height: 100%; + padding: 0 8px; +} + +#preview-area .margin-control.margin-control-disabled { + cursor: default; +} + +#preview-area .margin-control-line { + border-color: rgb(64, 128, 250); + border-style: dashed; + border-width: 1px; +} + +#preview-area .margin-control-disabled .margin-control-line { + border-color: gray; +} + +#preview-area .margin-control-top .margin-control-line, +#preview-area .margin-control-bottom .margin-control-line { + width: 100%; +} + +#preview-area .margin-control-left .margin-control-line, +#preview-area .margin-control-right .margin-control-line { + height: 100%; +} + +#preview-area .margin-control-textbox { + background-color: #2a2a2a; + border: 1px solid #888; + box-sizing: border-box; + color: white; + cursor: auto; + font-family: arial; + font-size: 0.8em; + height: 25px; + padding: 5px 0; + position: absolute; + text-align: center; + width: 60px; +} + +#preview-area .margin-control-textbox.invalid { + background-color: rgb(193, 27, 23); +} + +#preview-area .margin-control-top .margin-control-textbox { + left: 50%; + top: 8px; +} + +#preview-area .margin-control-right .margin-control-textbox { + right: 8px; + top: 50%; +} + +#preview-area .margin-control-bottom .margin-control-textbox { + bottom: 8px; + right: 50%; +} + +#preview-area .margin-control-left .margin-control-textbox { + bottom: 50%; + left: 8px; +} diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control.html b/chrome/browser/resources/print_preview/previewarea/margin_control.html new file mode 100644 index 0000000..58bbdbab --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/margin_control.html @@ -0,0 +1,6 @@ +<div id="margin-control-template" + class="margin-control invisible" + style="display: none;"> + <div class="margin-control-line"></div> + <input class="margin-control-textbox" type="text"/> +</div> diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control.js b/chrome/browser/resources/print_preview/previewarea/margin_control.js new file mode 100644 index 0000000..476371a --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/margin_control.js @@ -0,0 +1,467 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Draggable control for setting a page margin. + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Orientation of the margin control that determines where the margin + * textbox will be placed. + * @constructor + * @extends {print_preview.Component} + */ + function MarginControl(orientation) { + print_preview.Component.call(this); + + /** + * Determines where the margin textbox will be placed. + * @type {print_preview.ticket_items.CustomMargins.Orientation} + * @private + */ + this.orientation_ = orientation; + + /** + * Position of the margin control in points. + * @type {number} + * @private + */ + this.positionInPts_ = 0; + + /** + * Page size of the document to print. + * @type {!print_preview.Size} + * @private + */ + this.pageSize_ = new print_preview.Size(0, 0); + + /** + * Amount to scale pixel values by to convert to pixel space. + * @type {number} + * @private + */ + this.scaleTransform_ = 1; + + /** + * Amount to translate values in pixel space. + * @type {!print_preview.Coordinate2d} + * @private + */ + this.translateTransform_ = new print_preview.Coordinate2d(0, 0); + + /** + * Position of the margin control when dragging starts. + * @type {print_preview.Coordinate2d} + * @private + */ + this.marginStartPositionInPixels_ = null; + + /** + * Position of the mouse when the dragging starts. + * @type {print_preview.Coordinate2d} + * @private + */ + this.mouseStartPositionInPixels_ = null; + + /** + * Processing timeout for the textbox. + * @type {Object} + * @private + */ + this.textTimeout_ = null; + + /** + * Value of the textbox when the timeout was started. + * @type {?string} + * @private + */ + this.preTimeoutValue_ = null; + + /** + * Textbox used to display and receive the value of the margin. + * @type {HTMLInputElement} + * @private + */ + this.textbox_ = null; + + /** + * Element of the margin control line. + * @type {HTMLElement} + * @private + */ + this.marginLineEl_ = null; + + /** + * Whether this margin control's textbox has keyboard focus. + * @type {boolean} + * @private + */ + this.isFocused_ = false; + + /** + * Whether the margin control is in an error state. + * @type {boolean} + * @private + */ + this.isInError_ = false; + }; + + /** + * Event types dispatched by the margin control. + * @enum {string} + */ + MarginControl.EventType = { + // Dispatched when the margin control starts dragging. + DRAG_START: 'print_preview.MarginControl.DRAG_START', + + // Dispatched when the text in the margin control's textbox changes. + TEXT_CHANGE: 'print_preview.MarginControl.TEXT_CHANGE' + }; + + /** + * CSS classes used by this component. + * @enum {string} + * @private + */ + MarginControl.Classes_ = { + TOP: 'margin-control-top', + RIGHT: 'margin-control-right', + BOTTOM: 'margin-control-bottom', + LEFT: 'margin-control-left', + TEXTBOX: 'margin-control-textbox', + INVALID: 'invalid', + INVISIBLE: 'invisible', + DISABLED: 'margin-control-disabled', + DRAGGING: 'margin-control-dragging', + LINE: 'margin-control-line' + }; + + /** + * Map from orientation to CSS class name. + * @type {object.< + * print_preview.ticket_items.CustomMargins.Orientation, + * MarginControl.Classes_>} + * @private + */ + MarginControl.OrientationToClass_ = {}; + MarginControl.OrientationToClass_[ + print_preview.ticket_items.CustomMargins.Orientation.TOP] = + MarginControl.Classes_.TOP; + MarginControl.OrientationToClass_[ + print_preview.ticket_items.CustomMargins.Orientation.RIGHT] = + MarginControl.Classes_.RIGHT; + MarginControl.OrientationToClass_[ + print_preview.ticket_items.CustomMargins.Orientation.BOTTOM] = + MarginControl.Classes_.BOTTOM; + MarginControl.OrientationToClass_[ + print_preview.ticket_items.CustomMargins.Orientation.LEFT] = + MarginControl.Classes_.LEFT; + + /** + * Radius of the margin control in pixels. Padding of control + 1 for border. + * @type {number} + * @const + * @private + */ + MarginControl.RADIUS_ = 9; + + /** + * Timeout after a text change after which the text in the textbox is saved to + * the print ticket. Value in milliseconds. + * @type {number} + * @const + * @private + */ + MarginControl.TEXTBOX_TIMEOUT_ = 1000; + + MarginControl.prototype = { + __proto__: print_preview.Component.prototype, + + /** @return {boolean} Whether this margin control is in focus. */ + getIsFocused: function() { + return this.isFocused_; + }, + + /** + * @return {print_preview.ticket_items.CustomMargins.Orientation} + * Orientation of the margin control. + */ + getOrientation: function() { + return this.orientation_; + }, + + /** + * @param {number} scaleTransform New scale transform of the margin control. + */ + setScaleTransform: function(scaleTransform) { + this.scaleTransform_ = scaleTransform; + // Reset position + this.setPositionInPts(this.positionInPts_); + }, + + /** + * @param {!print_preview.Coordinate2d} translateTransform New translate + * transform of the margin control. + */ + setTranslateTransform: function(translateTransform) { + this.translateTransform_ = translateTransform; + // Reset position + this.setPositionInPts(this.positionInPts_); + }, + + /** + * @param {!print_preview.Size} pageSize New size of the document's pages. + */ + setPageSize: function(pageSize) { + this.pageSize_ = pageSize; + this.setPositionInPts(this.positionInPts_); + }, + + /** @param {boolean} isVisible Whether the margin control is visible. */ + setIsVisible: function(isVisible) { + if (isVisible) { + this.getElement().classList.remove(MarginControl.Classes_.INVISIBLE); + } else { + this.getElement().classList.add(MarginControl.Classes_.INVISIBLE); + } + }, + + /** @return {boolean} Whether the margin control is in an error state. */ + getIsInError: function() { + return this.isInError_; + }, + + /** + * @param {boolean} isInError Whether the margin control is in an error + * state. + */ + setIsInError: function(isInError) { + this.isInError_ = isInError; + if (isInError) { + this.textbox_.classList.add(MarginControl.Classes_.INVALID); + } else { + this.textbox_.classList.remove(MarginControl.Classes_.INVALID); + } + }, + + /** @param {boolean} isEnabled Whether to enable the margin control. */ + setIsEnabled: function(isEnabled) { + this.textbox_.disabled = !isEnabled; + if (isEnabled) { + this.getElement().classList.remove(MarginControl.Classes_.DISABLED); + } else { + this.getElement().classList.add(MarginControl.Classes_.DISABLED); + } + }, + + /** @return {number} Current position of the margin control in points. */ + getPositionInPts: function() { + return this.positionInPts_; + }, + + /** + * @param {number} posInPts New position of the margin control in points. + */ + setPositionInPts: function(posInPts) { + this.positionInPts_ = posInPts; + var orientationEnum = + print_preview.ticket_items.CustomMargins.Orientation; + var x = this.translateTransform_.x; + var y = this.translateTransform_.y; + var width = null, height = null; + if (this.orientation_ == orientationEnum.TOP) { + y = this.scaleTransform_ * posInPts + this.translateTransform_.y - + MarginControl.RADIUS_; + width = this.scaleTransform_ * this.pageSize_.width; + } else if (this.orientation_ == orientationEnum.RIGHT) { + x = this.scaleTransform_ * (this.pageSize_.width - posInPts) + + this.translateTransform_.x - MarginControl.RADIUS_; + height = this.scaleTransform_ * this.pageSize_.height; + } else if (this.orientation_ == orientationEnum.BOTTOM) { + y = this.scaleTransform_ * (this.pageSize_.height - posInPts) + + this.translateTransform_.y - MarginControl.RADIUS_; + width = this.scaleTransform_ * this.pageSize_.width; + } else { + x = this.scaleTransform_ * posInPts + this.translateTransform_.x - + MarginControl.RADIUS_; + height = this.scaleTransform_ * this.pageSize_.height; + } + this.getElement().style.left = Math.round(x) + 'px'; + this.getElement().style.top = Math.round(y) + 'px'; + if (width != null) { + this.getElement().style.width = Math.round(width) + 'px'; + } + if (height != null) { + this.getElement().style.height = Math.round(height) + 'px'; + } + }, + + /** @return {string} The value in the margin control's textbox. */ + getTextboxValue: function() { + return this.textbox_.value; + }, + + /** @param {string} value New value of the margin control's textbox. */ + setTextboxValue: function(value) { + if (this.textbox_.value != value) { + this.textbox_.value = value; + } + }, + + /** + * Converts a value in pixels to points. + * @param {number} Pixel value to convert. + * @return {number} Given value expressed in points. + */ + convertPixelsToPts: function(pixels) { + var pts; + var orientationEnum = + print_preview.ticket_items.CustomMargins.Orientation; + if (this.orientation_ == orientationEnum.TOP) { + pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_; + pts /= this.scaleTransform_; + } else if (this.orientation_ == orientationEnum.RIGHT) { + pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_; + pts /= this.scaleTransform_; + pts = this.pageSize_.width - pts; + } else if (this.orientation_ == orientationEnum.BOTTOM) { + pts = pixels - this.translateTransform_.y + MarginControl.RADIUS_; + pts /= this.scaleTransform_; + pts = this.pageSize_.height - pts; + } else { + pts = pixels - this.translateTransform_.x + MarginControl.RADIUS_; + pts /= this.scaleTransform_; + } + return pts; + }, + + /** + * Translates the position of the margin control relative to the mouse + * position in pixels. + * @param {!print_preview.Coordinate2d} mousePosition New position of + * the mouse. + * @return {!print_preview.Coordinate2d} New position of the margin control. + */ + translateMouseToPositionInPixels: function(mousePosition) { + return new print_preview.Coordinate2d( + mousePosition.x - this.mouseStartPositionInPixels_.x + + this.marginStartPositionInPixels_.x, + mousePosition.y - this.mouseStartPositionInPixels_.y + + this.marginStartPositionInPixels_.y); + }, + + /** @override */ + createDom: function() { + this.setElementInternal(this.cloneTemplateInternal( + 'margin-control-template')); + this.getElement().classList.add(MarginControl.OrientationToClass_[ + this.orientation_]); + this.textbox_ = this.getElement().getElementsByClassName( + MarginControl.Classes_.TEXTBOX)[0]; + this.marginLineEl_ = this.getElement().getElementsByClassName( + MarginControl.Classes_.LINE)[0]; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.getElement(), 'mousedown', this.onMouseDown_.bind(this)); + this.tracker.add( + this.textbox_, 'keydown', this.onTextboxKeyDown_.bind(this)); + this.tracker.add( + this.textbox_, 'focus', this.setIsFocused_.bind(this, true)); + this.tracker.add(this.textbox_, 'blur', this.onTexboxBlur_.bind(this)); + }, + + /** @override */ + exitDocument: function() { + print_preview.Component.prototype.exitDocument.call(this); + this.textbox_ = null; + this.marginLineEl_ = null; + }, + + /** + * @param {boolean} isFocused Whether the margin control is in focus. + * @private + */ + setIsFocused_: function(isFocused) { + this.isFocused_ = isFocused; + }, + + /** + * Called whenever a mousedown event occurs on the component. + * @param {MouseEvent} event The event that occured. + * @private + */ + onMouseDown_: function(event) { + if (!this.textbox_.disabled && + event.button == 0 && + (event.target == this.getElement() || + event.target == this.marginLineEl_)) { + this.mouseStartPositionInPixels_ = + new print_preview.Coordinate2d(event.x, event.y); + this.marginStartPositionInPixels_ = new print_preview.Coordinate2d( + this.getElement().offsetLeft, this.getElement().offsetTop); + this.setIsInError(false); + cr.dispatchSimpleEvent(this, MarginControl.EventType.DRAG_START); + } + }, + + /** + * Called when a key down event occurs on the textbox. Dispatches a + * TEXT_CHANGE event if the "Enter" key was pressed. + * @param {Event} event Contains the key that was pressed. + * @private + */ + onTextboxKeyDown_: function(event) { + if (this.textTimeout_) { + clearTimeout(this.textTimeout_); + this.textTimeout_ = null; + } + if (event.keyIdentifier == 'Enter') { + this.preTimeoutValue_ = null; + cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); + } else { + if (this.preTimeoutValue_ == null) { + this.preTimeoutValue_ = this.textbox_.value; + } + this.textTimeout_ = setTimeout( + this.onTextboxTimeout_.bind(this), MarginControl.TEXTBOX_TIMEOUT_); + } + }, + + /** + * Called after a timeout after the text in the textbox has changed. Saves + * the textbox's value to the print ticket. + * @private + */ + onTextboxTimeout_: function() { + this.textTimeout_ = null; + if (this.textbox_.value != this.preTimeoutValue_) { + cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); + } + this.preTimeoutValue_ = null; + }, + + /** + * Called when the textbox loses focus. Dispatches a TEXT_CHANGE event. + */ + onTexboxBlur_: function() { + if (this.textTimeout_) { + clearTimeout(this.textTimeout_); + this.textTimeout_ = null; + this.preTimeoutValue_ = null; + } + this.setIsFocused_(false); + cr.dispatchSimpleEvent(this, MarginControl.EventType.TEXT_CHANGE); + } + }; + + // Export + return { + MarginControl: MarginControl + }; +}); diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control_container.css b/chrome/browser/resources/print_preview/previewarea/margin_control_container.css new file mode 100644 index 0000000..bfb1935 --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/margin_control_container.css @@ -0,0 +1,12 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +.margin-control-container-dragging-vertical { + cursor: ns-resize; +} + +.margin-control-container-dragging-horizontal { + cursor: ew-resize; +} diff --git a/chrome/browser/resources/print_preview/previewarea/margin_control_container.js b/chrome/browser/resources/print_preview/previewarea/margin_control_container.js new file mode 100644 index 0000000..1209da9 --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/margin_control_container.js @@ -0,0 +1,446 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * UI component used for setting custom print margins. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and + * write custom margin values. + * @constructor + * @extends {print_preview.Component} + */ + function MarginControlContainer(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used to read and write custom margin values. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Used to convert between the system's local units and points. + * @type {!print_preview.MeasurementSystem} + * @private + */ + this.measurementSystem_ = printTicketStore.measurementSystem; + + /** + * Convenience array that contains all of the margin controls. + * @type {!Object< + * print_preview.ticket_items.CustomMargins.Orientation, + * !print_preview.MarginControl>} + * @private + */ + this.controls_ = {}; + for (var key in print_preview.ticket_items.CustomMargins.Orientation) { + var orientation = print_preview.ticket_items.CustomMargins.Orientation[ + key]; + var control = new print_preview.MarginControl(orientation); + this.controls_[orientation] = control; + this.addChild(control); + } + + /** + * Margin control currently being dragged. Null if no control is being + * dragged. + * @type {print_preview.MarginControl} + * @private + */ + this.draggedControl_ = null; + + /** + * Translation transformation in pixels to translate from the origin of the + * custom margins component to the top-left corner of the most visible + * preview page. + * @type {!print_preview.Coordinate2d} + * @private + */ + this.translateTransform_ = new print_preview.Coordinate2d(0, 0); + + /** + * Scaling transformation to scale from pixels to the units which the + * print preview is in. The scaling factor is the same in both dimensions, + * so this field is just a single number. + * @type {number} + * @private + */ + this.scaleTransform_ = 1; + + /** + * Clipping size for clipping the margin controls. + * @type {print_preview.Size} + * @private + */ + this.clippingSize_ = null; + }; + + /** + * CSS classes used by the custom margins component. + * @enum {string} + * @private + */ + MarginControlContainer.Classes_ = { + DRAGGING_HORIZONTAL: 'margin-control-container-dragging-horizontal', + DRAGGING_VERTICAL: 'margin-control-container-dragging-vertical' + }; + + /** + * @param {print_preview.ticket_items.CustomMargins.Orientation} orientation + * Orientation value to test. + * @return {boolean} Whether the given orientation is TOP or BOTTOM. + * @private + */ + MarginControlContainer.isTopOrBottom_ = function(orientation) { + return orientation == + print_preview.ticket_items.CustomMargins.Orientation.TOP || + orientation == + print_preview.ticket_items.CustomMargins.Orientation.BOTTOM; + }; + + MarginControlContainer.prototype = { + __proto__: print_preview.Component.prototype, + + /** + * Updates the translation transformation that translates pixel values in + * the space of the HTML DOM. + * @param {print_preview.Coordinate2d} translateTransform Updated value of + * the translation transformation. + */ + updateTranslationTransform: function(translateTransform) { + if (!translateTransform.equals(this.translateTransform_)) { + this.translateTransform_ = translateTransform; + for (var orientation in this.controls_) { + this.controls_[orientation].setTranslateTransform(translateTransform); + } + } + }, + + /** + * Updates the scaling transform that scales pixels values to point values. + * @param {number} scaleTransform Updated value of the scale transform. + */ + updateScaleTransform: function(scaleTransform) { + if (scaleTransform != this.scaleTransform_) { + this.scaleTransform_ = scaleTransform; + for (var orientation in this.controls_) { + this.controls_[orientation].setScaleTransform(scaleTransform); + } + } + }, + + /** + * Clips margin controls to the given clip size in pixels. + * @param {print_preview.Size} Size to clip the margin controls to. + */ + updateClippingMask: function(clipSize) { + if (!clipSize) { + return; + } + this.clippingSize_ = clipSize; + for (var orientation in this.controls_) { + var el = this.controls_[orientation].getElement(); + el.style.clip = 'rect(' + + (-el.offsetTop) + 'px, ' + + (clipSize.width - el.offsetLeft) + 'px, ' + + (clipSize.height - el.offsetTop) + 'px, ' + + (-el.offsetLeft) + 'px)'; + } + }, + + /** Shows the margin controls if the need to be shown. */ + showMarginControlsIfNeeded: function() { + if (this.printTicketStore_.getMarginsType() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + this.setIsMarginControlsVisible_(true); + } + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + + // We want to respond to mouse up events even beyond the component's + // element. + this.tracker.add(window, 'mouseup', this.onMouseUp_.bind(this)); + this.tracker.add(window, 'mousemove', this.onMouseMove_.bind(this)); + this.tracker.add( + this.getElement(), 'mouseover', this.onMouseOver_.bind(this)); + this.tracker.add( + this.getElement(), 'mouseout', this.onMouseOut_.bind(this)); + + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onTicketChange_.bind(this)); + + for (var orientation in this.controls_) { + this.tracker.add( + this.controls_[orientation], + print_preview.MarginControl.EventType.DRAG_START, + this.onControlDragStart_.bind(this, this.controls_[orientation])); + this.tracker.add( + this.controls_[orientation], + print_preview.MarginControl.EventType.TEXT_CHANGE, + this.onControlTextChange_.bind(this, this.controls_[orientation])); + } + }, + + /** @override */ + decorateInternal: function() { + for (var orientation in this.controls_) { + this.controls_[orientation].render(this.getElement()); + } + }, + + /** + * @param {boolean} isVisible Whether the margin controls are visible. + * @private + */ + setIsMarginControlsVisible_: function(isVisible) { + for (var orientation in this.controls_) { + this.controls_[orientation].setIsVisible(isVisible); + } + }, + + /** + * Moves the position of the given control to the desired position in + * pixels within some constraint minimum and maximum. + * @param {!print_preview.MarginControl} control Control to move. + * @param {!print_preview.Coordinate2d} posInPixels Desired position to move + * to in pixels. + * @private + */ + moveControlWithConstraints_: function(control, posInPixels) { + var newPosInPts; + if (MarginControlContainer.isTopOrBottom_(control.getOrientation())) { + newPosInPts = control.convertPixelsToPts(posInPixels.y); + } else { + newPosInPts = control.convertPixelsToPts(posInPixels.x); + } + newPosInPts = Math.min( + this.printTicketStore_.getCustomMarginMax(control.getOrientation()), + newPosInPts); + newPosInPts = Math.max(0, newPosInPts); + newPosInPts = Math.round(newPosInPts); + control.setPositionInPts(newPosInPts); + control.setTextboxValue(this.serializeValueFromPts_(newPosInPts)); + }, + + /** + * @param {string} value Value to parse to points. E.g. '3.40"' or '200mm'. + * @return {number} Value in points represented by the input value. + * @private + */ + parseValueToPts_: function(value) { + // Removing whitespace anywhere in the string. + value = value.replace(/\s*/g, ''); + if (value.length == 0) { + return null; + } + var validationRegex = new RegExp('^(^-?)(\\d)+(\\' + + this.measurementSystem_.thousandsDelimeter + '\\d{3})*(\\' + + this.measurementSystem_.decimalDelimeter + '\\d*)?' + + '(' + this.measurementSystem_.unitSymbol + ')?$'); + if (validationRegex.test(value)) { + // Replacing decimal point with the dot symbol in order to use + // parseFloat() properly. + var replacementRegex = + new RegExp('\\' + this.measurementSystem_.decimalDelimeter + '{1}'); + value = value.replace(replacementRegex, '.'); + return this.measurementSystem_.convertToPoints(parseFloat(value)); + } + return null; + }, + + /** + * @param {number} value Value in points to serialize. + * @return {string} String representation of the value in the system's local + * units. + * @private + */ + serializeValueFromPts_: function(value) { + value = this.measurementSystem_.convertFromPoints(value); + value = this.measurementSystem_.roundValue(value); + return value + this.measurementSystem_.unitSymbol; + }, + + /** + * Called when a margin control starts to drag. + * @param {print_preview.MarginControl} control The control which started to + * drag. + * @private + */ + onControlDragStart_: function(control) { + this.draggedControl_ = control; + this.getElement().classList.add( + MarginControlContainer.isTopOrBottom_(control.getOrientation()) ? + MarginControlContainer.Classes_.DRAGGING_VERTICAL : + MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); + }, + + /** + * Called when the mouse moves in the custom margins component. Moves the + * dragged margin control. + * @param {MouseEvent} event Contains the position of the mouse. + * @private + */ + onMouseMove_: function(event) { + if (this.draggedControl_) { + this.moveControlWithConstraints_( + this.draggedControl_, + this.draggedControl_.translateMouseToPositionInPixels( + new print_preview.Coordinate2d(event.x, event.y))); + this.updateClippingMask(this.clippingSize_); + } + }, + + /** + * Called when the mouse is released in the custom margins component. + * Releases the dragged margin control. + * @param {MouseEvent} event Contains the position of the mouse. + * @private + */ + onMouseUp_: function(event) { + if (this.draggedControl_) { + this.getElement().classList.remove( + MarginControlContainer.Classes_.DRAGGING_VERTICAL); + this.getElement().classList.remove( + MarginControlContainer.Classes_.DRAGGING_HORIZONTAL); + if (event) { + var posInPixels = + this.draggedControl_.translateMouseToPositionInPixels( + new print_preview.Coordinate2d(event.x, event.y)); + this.moveControlWithConstraints_(this.draggedControl_, posInPixels); + } + this.updateClippingMask(this.clippingSize_); + this.printTicketStore_.updateCustomMargin( + this.draggedControl_.getOrientation(), + this.draggedControl_.getPositionInPts()); + this.draggedControl_ = null; + } + }, + + /** + * Called when the mouse moves onto the component. Shows the margin + * controls. + * @private + */ + onMouseOver_: function() { + var fromElement = event.fromElement; + while (fromElement != null) { + if (fromElement == this.getElement()) { + return; + } + fromElement = fromElement.parentElement; + } + if (this.printTicketStore_.hasMarginsCapability() && + this.printTicketStore_.getMarginsType() == + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + this.setIsMarginControlsVisible_(true); + } + }, + + /** + * Called when the mouse moves off of the component. Hides the margin + * controls. + * @private + */ + onMouseOut_: function(event) { + var toElement = event.toElement; + while (toElement != null) { + if (toElement == this.getElement()) { + return; + } + toElement = toElement.parentElement; + } + if (this.draggedControl_ != null) { + return; + } + for (var orientation in this.controls_) { + if (this.controls_[orientation].getIsFocused() || + this.controls_[orientation].getIsInError()) { + return; + } + } + this.setIsMarginControlsVisible_(false); + }, + + /** + * Called when the print ticket changes. Updates the position of the margin + * controls. + * @private + */ + onTicketChange_: function() { + var margins = this.printTicketStore_.getCustomMargins(); + for (var orientation in this.controls_) { + var control = this.controls_[orientation]; + control.setPageSize(this.printTicketStore_.pageSize); + control.setTextboxValue( + this.serializeValueFromPts_(margins.get(orientation))); + control.setPositionInPts(margins.get(orientation)); + control.setIsInError(false); + control.setIsEnabled(true); + } + this.updateClippingMask(this.clippingSize_); + if (this.printTicketStore_.getMarginsType() != + print_preview.ticket_items.MarginsType.Value.CUSTOM) { + this.setIsMarginControlsVisible_(false); + } + }, + + /** + * Called when the text in a textbox of a margin control changes or the + * textbox loses focus. + * Updates the print ticket store. + * @param {!print_preview.MarginControl} control Updated control. + * @private + */ + onControlTextChange_: function(control) { + var marginValue = this.parseValueToPts_(control.getTextboxValue()); + if (marginValue != null) { + this.printTicketStore_.updateCustomMargin( + control.getOrientation(), marginValue); + } else { + var enableOtherControls; + if (!control.getIsFocused()) { + // If control no longer in focus, revert to previous valid value. + control.setTextboxValue( + this.serializeValueFromPts_(control.getPositionInPts())); + control.setIsInError(false); + enableOtherControls = true; + } else { + control.setIsInError(true); + enableOtherControls = false; + } + // Enable other controls. + for (var o in this.controls_) { + if (control.getOrientation() != o) { + this.controls_[o].setIsEnabled(enableOtherControls); + } + } + } + } + }; + + // Export + return { + MarginControlContainer: MarginControlContainer + }; +}); diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.css b/chrome/browser/resources/print_preview/previewarea/preview_area.css new file mode 100644 index 0000000..137f914 --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/preview_area.css @@ -0,0 +1,75 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#preview-area.preview-area { + -webkit-box-flex: 1; + -webkit-user-select: none; + background-color: #ccc; + height: 100%; + overflow: hidden; + position: relative; +} + +#preview-area .preview-area-plugin { + /* pluginFadeInTransitionDuration = 200ms */ + -webkit-transition: opacity 200ms linear; + /* pluginFadeInTransitionDelay = overlayFadeOutTransitionDuration = 100ms */ + -webkit-transition-delay: 100ms; + cursor: inherit; + height: 100%; + opacity: 1; + width: 100%; +} + +#preview-area .preview-area-plugin.invisible { + /* pluginFadeOutTransitionDuration = 100ms */ + -webkit-transition: opacity 100ms linear; + /* pluginFadeOutTransitionDelay = 250ms */ + -webkit-transition-delay: 250ms; + opacity: 0; +} + +#preview-area .preview-area-overlay-layer { + -webkit-transition: opacity 200ms linear; + /* overlayFadeInTransitionDelay = pluginFadeOutTransitionDelay + + * pluginFadeOutTransitionDuration = 350ms */ + -webkit-transition-delay: 350ms; + -webkit-user-select: none; + background: #ccc; + height: 100%; + margin: 0; + opacity: 1; + position: absolute; + width: 100%; + z-index: 1; +} + +#preview-area .preview-area-overlay-layer.invisible { + /* overlayFadeOutTransitionDuration = 100ms */ + -webkit-transition: opacity 100ms linear; + opacity: 0; + pointer-events: none; +} + +#preview-area .preview-area-messages { + height: 100%; +} + +#preview-area .preview-area-message { + color: #404040; + font-size: 1.1em; + position: relative; + text-align: center; + text-shadow: 0 1px 0 rgba(255, 255, 255, .5); + top: 50%; +} + +#preview-area .preview-area-no-plugin-action-area { + margin-top: 12px; +} + +#preview-area .preview-area-open-system-dialog-button-throbber { + vertical-align: middle; +} diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.html b/chrome/browser/resources/print_preview/previewarea/preview_area.html new file mode 100644 index 0000000..39b9d81 --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/preview_area.html @@ -0,0 +1,41 @@ +<div id="preview-area" class="preview-area"> + <div class="preview-area-overlay-layer"> + <div class="preview-area-messages"> + + <div class="preview-area-loading-message preview-area-message"> + <span i18n-content="loading"></span> + <span class="preview-area-loading-message-jumping-dots jumping-dots" + ><span>.</span><span>.</span><span>.</span></span> + </div> + + <div class="preview-area-custom-message preview-area-message" + style="display: none;"> + <div class="preview-area-custom-message-text"></div> + <div class="preview-area-custom-action-area"> + <button class="preview-area-open-system-dialog-button" + i18n-content="launchNativeDialog"></button> + <div class="preview-area-open-system-dialog-button-throbber throbber" + style="display: none;"></div> + </div> + </div> + + <div class="preview-area-preview-failed-message preview-area-message" + i18n-content="previewFailed" + style="display: none;"></div> + + <div class="preview-area-print-failed preview-area-message" style="display: none;"> + <div i18n-content="invalidPrinterSettings"></div> + <div class="preview-area-print-failed-action-area"> + <button class="preview-area-open-system-dialog-button" + i18n-content="launchNativeDialog"></button> + <div class="preview-area-open-system-dialog-button-throbber throbber" + style="display: none;"></div> + </div> + </div> + + </div> + </div> + <object class="preview-area-compatibility-object" + type="application/x-google-chrome-print-preview-pdf" + data="chrome://print/dummy.pdf"></object> +</div> diff --git a/chrome/browser/resources/print_preview/previewarea/preview_area.js b/chrome/browser/resources/print_preview/previewarea/preview_area.js new file mode 100644 index 0000000..1ede80a --- /dev/null +++ b/chrome/browser/resources/print_preview/previewarea/preview_area.js @@ -0,0 +1,592 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a PreviewArea object. It represents the area where the preview + * document is displayed. + * @param {!print_preview.DestinationStore} destinationStore Used to get the + * currently selected destination. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to get + * information about how the preview should be displayed. + * @param {!print_preview.NativeLayer} nativeLayer Needed to communicate with + * Chromium's preview generation system. + * @constructor + * @extends {print_preview.Component} + */ + function PreviewArea(destinationStore, printTicketStore, nativeLayer) { + print_preview.Component.call(this); + + /** + * Used to get the currently selected destination. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + + /** + * Used to get information about how the preview should be displayed. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Used to contruct the preview generator. + * @type {!print_preview.NativeLayer} + * @private + */ + this.nativeLayer_ = nativeLayer; + + /** + * Used to read generated page previews. + * @type {print_preview.PreviewGenerator} + * @private + */ + this.previewGenerator_ = null; + + /** + * The embedded pdf plugin object. It's value is null if not yet loaded. + * @type {HTMLEmbedElement} + * @private + */ + this.plugin_ = null; + + /** + * Custom margins component superimposed on the preview plugin. + * @type {!print_preview.MarginControlContainer} + * @private + */ + this.marginControlContainer_ = + new print_preview.MarginControlContainer(this.printTicketStore_); + this.addChild(this.marginControlContainer_); + + /** + * Current zoom level as a percentage. + * @type {?number} + * @private + */ + this.zoomLevel_ = null; + + /** + * Current page offset which can be used to calculate scroll amount. + * @type {print_preview.Coordinate2d} + * @private + */ + this.pageOffset_ = null; + + /** + * Whether the plugin has finished reloading. + * @type {boolean} + * @private + */ + this.isPluginReloaded_ = false; + + /** + * Whether the document preview is ready. + * @type {boolean} + * @private + */ + this.isDocumentReady_ = false; + + /** + * Timeout object used to display a loading message if the preview is taking + * a long time to generate. + * @type {Object} + * @private + */ + this.loadingTimeout_ = null; + + /** + * Overlay element. + * @type {HTMLElement} + * @private + */ + this.overlayEl_ = null; + + /** + * The "Open system dialog" button. + * @type {HTMLButtonElement} + * @private + */ + this.openSystemDialogButton_ = null; + }; + + /** + * Event types dispatched by the preview area. + * @enum {string} + */ + PreviewArea.EventType = { + // Dispatched when the "Open system dialog" button is clicked. + OPEN_SYSTEM_DIALOG_CLICK: + 'print_preview.PreviewArea.OPEN_SYSTEM_DIALOG_CLICK', + + // Dispatched when the document preview is complete. + PREVIEW_GENERATION_DONE: + 'print_preview.PreviewArea.PREVIEW_GENERATION_DONE', + + // Dispatched when the document preview failed to be generated. + PREVIEW_GENERATION_FAIL: + 'print_preview.PreviewArea.PREVIEW_GENERATION_FAIL', + + // Dispatched when a new document preview is being generated. + PREVIEW_GENERATION_IN_PROGRESS: + 'print_preview.PreviewArea.PREVIEW_GENERATION_IN_PROGRESS' + }; + + /** + * CSS classes used by the preview area. + * @enum {string} + * @private + */ + PreviewArea.Classes_ = { + COMPATIBILITY_OBJECT: 'preview-area-compatibility-object', + CUSTOM_MESSAGE_TEXT: 'preview-area-custom-message-text', + MESSAGE: 'preview-area-message', + INVISIBLE: 'invisible', + OPEN_SYSTEM_DIALOG_BUTTON: 'preview-area-open-system-dialog-button', + OPEN_SYSTEM_DIALOG_BUTTON_THROBBER: + 'preview-area-open-system-dialog-button-throbber', + OVERLAY: 'preview-area-overlay-layer', + PDF_PLUGIN: 'preview-area-pdf-plugin' + }; + + /** + * Enumeration of IDs shown in the preview area. + * @enum {string} + * @private + */ + PreviewArea.MessageId_ = { + CUSTOM: 'custom', + LOADING: 'loading', + PREVIEW_FAILED: 'preview-failed' + }; + + /** + * Maps message IDs to the CSS class that contains them. + * @type {object.<PreviewArea.MessageId_, string>} + * @private + */ + PreviewArea.MessageIdClassMap_ = {}; + PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.CUSTOM] = + 'preview-area-custom-message'; + PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.LOADING] = + 'preview-area-loading-message'; + PreviewArea.MessageIdClassMap_[PreviewArea.MessageId_.PREVIEW_FAILED] = + 'preview-area-preview-failed-message'; + + /** + * Amount of time in milliseconds to wait after issueing a new preview before + * the loading message is shown. + * @type {number} + * @const + * @private + */ + PreviewArea.LOADING_TIMEOUT_ = 200; + + PreviewArea.prototype = { + __proto__: print_preview.Component.prototype, + + /** + * Should only be called after calling this.render(). + * @return {boolean} Whether the preview area has a compatible plugin to + * display the print preview in. + */ + get hasCompatiblePlugin() { + return this.previewGenerator_ != null; + }, + + /** + * Processes a keyboard event that could possibly be used to change state of + * the preview plugin. + * @param {MouseEvent} e Mouse event to process. + */ + handleDirectionalKeyEvent: function(e) { + // Make sure the PDF plugin is there. + // We only care about: PageUp, PageDown, Left, Up, Right, Down. + // If the user is holding a modifier key, ignore. + if (!this.plugin_ || + !arrayContains([33, 34, 37, 38, 39, 40], e.keyCode) || + e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) { + return; + } + + // Don't handle the key event for these elements. + var tagName = document.activeElement.tagName; + if (arrayContains(['INPUT', 'SELECT', 'EMBED'], tagName)) { + return; + } + + // For the most part, if any div of header was the last clicked element, + // then the active element is the body. Starting with the last clicked + // element, and work up the DOM tree to see if any element has a + // scrollbar. If there exists a scrollbar, do not handle the key event + // here. + var element = e.target; + while (element) { + if (element.scrollHeight > element.clientHeight || + element.scrollWidth > element.clientWidth) { + return; + } + element = element.parentElement; + } + + // No scroll bar anywhere, or the active element is something else, like a + // button. Note: buttons have a bigger scrollHeight than clientHeight. + this.plugin_.sendKeyEvent(e.keyCode); + e.preventDefault(); + }, + + /** + * Shows a custom message on the preview area's overlay. + * @param {string} message Custom message to show. + */ + showCustomMessage: function(message) { + this.showMessage_(PreviewArea.MessageId_.CUSTOM, message); + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.openSystemDialogButton_, + 'click', + this.onOpenSystemDialogButtonClick_.bind(this)); + + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onTicketChange_.bind(this)); + + if (this.checkPluginCompatibility_()) { + this.previewGenerator_ = new print_preview.PreviewGenerator( + this.destinationStore_, this.printTicketStore_, this.nativeLayer_); + this.tracker.add( + this.previewGenerator_, + print_preview.PreviewGenerator.EventType.PREVIEW_START, + this.onPreviewStart_.bind(this)); + this.tracker.add( + this.previewGenerator_, + print_preview.PreviewGenerator.EventType.PAGE_READY, + this.onPagePreviewReady_.bind(this)); + this.tracker.add( + this.previewGenerator_, + print_preview.PreviewGenerator.EventType.FAIL, + this.onPreviewGenerationFail_.bind(this)); + this.tracker.add( + this.previewGenerator_, + print_preview.PreviewGenerator.EventType.DOCUMENT_READY, + this.onDocumentReady_.bind(this)); + } else { + this.showCustomMessage(localStrings.getString('noPlugin')); + } + }, + + /** @override */ + exitDocument: function() { + print_preview.Component.prototype.exitDocument.call(this); + if (this.previewGenerator_) { + this.previewGenerator_.removeEventListeners(); + } + this.overlayEl_ = null; + this.openSystemDialogButton_ = null; + }, + + /** @override */ + decorateInternal: function() { + this.marginControlContainer_.decorate(this.getElement()); + this.overlayEl_ = this.getElement().getElementsByClassName( + PreviewArea.Classes_.OVERLAY)[0]; + this.openSystemDialogButton_ = this.getElement().getElementsByClassName( + PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON)[0]; + }, + + /** + * Checks to see if a suitable plugin for rendering the preview exists. If + * one does not exist, then an error message will be displayed. + * @return {boolean} Whether Chromium has a suitable plugin for rendering + * the preview. + * @private + */ + checkPluginCompatibility_: function() { + var compatObj = this.getElement().getElementsByClassName( + PreviewArea.Classes_.COMPATIBILITY_OBJECT)[0]; + var isCompatible = + compatObj.onload && + compatObj.goToPage && + compatObj.removePrintButton && + compatObj.loadPreviewPage && + compatObj.printPreviewPageCount && + compatObj.resetPrintPreviewUrl && + compatObj.onPluginSizeChanged && + compatObj.onScroll && + compatObj.pageXOffset && + compatObj.pageYOffset && + compatObj.setZoomLevel && + compatObj.setPageNumbers && + compatObj.setPageXOffset && + compatObj.setPageYOffset && + compatObj.getHorizontalScrollbarThickness && + compatObj.getVerticalScrollbarThickness && + compatObj.getPageLocationNormalized && + compatObj.getHeight && + compatObj.getWidth; + compatObj.parentElement.removeChild(compatObj); + return isCompatible; + }, + + /** + * Shows a given message on the overlay. + * @param {print_preview.PreviewArea.MessageId_} messageId ID of the message + * to show. + * @param {string=} opt_message Optional message to show that can be used + * by some message IDs. + * @private + */ + showMessage_: function(messageId, opt_message) { + // Hide all messages. + var messageEls = this.getElement().getElementsByClassName( + PreviewArea.Classes_.MESSAGE); + for (var i = 0, messageEl; messageEl = messageEls[i]; i++) { + setIsVisible(messageEl, false); + } + // Disable jumping animation to conserve cycles. + var jumpingDotsEl = this.getElement().querySelector( + '.preview-area-loading-message-jumping-dots'); + jumpingDotsEl.classList.remove('jumping-dots'); + + // Show specific message. + if (messageId == PreviewArea.MessageId_.CUSTOM) { + var customMessageTextEl = this.getElement().getElementsByClassName( + PreviewArea.Classes_.CUSTOM_MESSAGE_TEXT)[0]; + customMessageTextEl.textContent = opt_message; + } else if (messageId == PreviewArea.MessageId_.LOADING) { + jumpingDotsEl.classList.add('jumping-dots'); + } + var messageEl = this.getElement().getElementsByClassName( + PreviewArea.MessageIdClassMap_[messageId])[0]; + setIsVisible(messageEl, true); + + // Show overlay. + this.overlayEl_.classList.remove(PreviewArea.Classes_.INVISIBLE); + }, + + /** + * Hides the message overlay. + * @private + */ + hideOverlay_: function() { + this.overlayEl_.classList.add(PreviewArea.Classes_.INVISIBLE); + // Disable jumping animation to conserve cycles. + var jumpingDotsEl = this.getElement().querySelector( + '.preview-area-loading-message-jumping-dots'); + jumpingDotsEl.classList.remove('jumping-dots'); + }, + + /** + * Creates a preview plugin and adds it to the DOM. + * @param {string} srcUrl Initial URL of the plugin. + * @private + */ + createPlugin_: function(srcUrl) { + if (this.plugin_) { + console.warn('Pdf preview plugin already created'); + return; + } + this.plugin_ = document.createElement('embed'); + // NOTE: The plugin's 'id' field must be set to 'pdf-viewer' since + // chrome/renderer/print_web_view_helper.cc actually references it. + this.plugin_.setAttribute('id', 'pdf-viewer'); + this.plugin_.setAttribute('class', 'preview-area-plugin'); + this.plugin_.setAttribute( + 'type', 'application/x-google-chrome-print-preview-pdf'); + this.plugin_.setAttribute('src', srcUrl); + this.plugin_.setAttribute('aria-live', 'polite'); + this.plugin_.setAttribute('aria-atomic', 'true'); + this.getElement().appendChild(this.plugin_); + + global['onPreviewPluginLoad'] = this.onPluginLoad_.bind(this); + this.plugin_.onload('onPreviewPluginLoad()'); + + global['onPreviewPluginVisualStateChange'] = + this.onPreviewVisualStateChange_.bind(this); + this.plugin_.onScroll('onPreviewPluginVisualStateChange()'); + this.plugin_.onPluginSizeChanged('onPreviewPluginVisualStateChange()'); + + this.plugin_.removePrintButton(); + this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled()); + }, + + /** + * Dispatches a PREVIEW_GENERATION_DONE event if all conditions are met. + * @private + */ + dispatchPreviewGenerationDoneIfReady_: function() { + if (this.isDocumentReady_ && this.isPluginReloaded_) { + cr.dispatchSimpleEvent( + this, PreviewArea.EventType.PREVIEW_GENERATION_DONE); + this.marginControlContainer_.showMarginControlsIfNeeded(); + } + }, + + /** + * Called when the open-system-dialog button is clicked. Disables the + * button, shows the throbber, and dispatches the OPEN_SYSTEM_DIALOG_CLICK + * event. + * @private + */ + onOpenSystemDialogButtonClick_: function() { + this.openSystemDialogButton_.disabled = true; + var openSystemDialogThrobber = this.getElement().getElementsByClassName( + PreviewArea.Classes_.OPEN_SYSTEM_DIALOG_BUTTON_THROBBER)[0]; + setIsVisible(openSystemDialogThrobber, true); + cr.dispatchSimpleEvent( + this, PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK); + }, + + /** + * Called when the print ticket changes. Updates the preview. + * @private + */ + onTicketChange_: function() { + if (this.previewGenerator_ && this.previewGenerator_.requestPreview()) { + if (this.loadingTimeout_ == null) { + this.loadingTimeout_ = setTimeout( + this.showMessage_.bind(this, PreviewArea.MessageId_.LOADING), + PreviewArea.LOADING_TIMEOUT_); + } + } else { + this.marginControlContainer_.showMarginControlsIfNeeded(); + } + }, + + /** + * Called when the preview generator begins loading the preview. + * @param {cr.Event} Contains the URL to initialize the plugin to. + * @private + */ + onPreviewStart_: function(event) { + this.isDocumentReady_ = false; + this.isPluginReloaded_ = false; + if (!this.plugin_) { + this.createPlugin_(event.previewUrl); + } + this.plugin_.goToPage('0'); + this.plugin_.resetPrintPreviewUrl(event.previewUrl); + this.plugin_.reload(); + this.plugin_.grayscale(!this.printTicketStore_.isColorEnabled()); + cr.dispatchSimpleEvent( + this, PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS); + }, + + /** + * Called when a page preview has been generated. Updates the plugin with + * the new page. + * @param {cr.Event} event Contains information about the page preview. + * @private + */ + onPagePreviewReady_: function(event) { + this.plugin_.loadPreviewPage(event.previewUrl, event.previewIndex); + }, + + /** + * Called when the preview generation is complete and the document is ready + * to print. + * @private + */ + onDocumentReady_: function(event) { + this.isDocumentReady_ = true; + this.dispatchPreviewGenerationDoneIfReady_(); + }, + + /** + * Called when the generation of a preview fails. Shows an error message. + * @private + */ + onPreviewGenerationFail_: function() { + this.showMessage_(PreviewArea.MessageId_.PREVIEW_FAILED); + cr.dispatchSimpleEvent( + this, PreviewArea.EventType.PREVIEW_GENERATION_FAIL); + }, + + /** + * Called when the plugin loads. This is a consequence of calling + * plugin.reload(). Certain plugin state can only be set after the plugin + * has loaded. + * @private + */ + onPluginLoad_: function() { + if (this.loadingTimeout_) { + clearTimeout(this.loadingTimeout_); + this.loadingTimeout_ = null; + } + // Setting the plugin's page count can only be called after the plugin is + // loaded and the document must be modifiable. + if (this.printTicketStore_.isDocumentModifiable) { + this.plugin_.printPreviewPageCount( + this.printTicketStore_.getPageNumberSet().size); + } + this.plugin_.setPageNumbers(JSON.stringify( + this.printTicketStore_.getPageNumberSet().asArray())); + if (this.zoomLevel_ != null && this.pageOffset_ != null) { + this.plugin_.setZoomLevel(this.zoomLevel_); + this.plugin_.setPageXOffset(this.pageOffset_.x); + this.plugin_.setPageYOffset(this.pageOffset_.y); + } else { + this.plugin_.fitToHeight(); + } + this.hideOverlay_(); + this.isPluginReloaded_ = true; + this.dispatchPreviewGenerationDoneIfReady_(); + }, + + /** + * Called when the preview plugin's visual state has changed. This is a + * consequence of scrolling or zooming the plugin. Updates the custom + * margins component if shown. + * @private + */ + onPreviewVisualStateChange_: function() { + if (this.isPluginReloaded_) { + this.zoomLevel_ = this.plugin_.getZoomLevel(); + this.pageOffset_ = new print_preview.Coordinate2d( + this.plugin_.pageXOffset(), this.plugin_.pageYOffset()); + } + var normalized = this.plugin_.getPageLocationNormalized().split(';'); + var pluginWidth = this.plugin_.getWidth(); + var pluginHeight = this.plugin_.getHeight(); + var translationTransform = new print_preview.Coordinate2d( + parseFloat(normalized[0]) * pluginWidth, + parseFloat(normalized[1]) * pluginHeight); + this.marginControlContainer_.updateTranslationTransform( + translationTransform); + var pageWidthInPixels = parseFloat(normalized[2]) * pluginWidth; + this.marginControlContainer_.updateScaleTransform( + pageWidthInPixels / this.printTicketStore_.pageSize.width); + this.marginControlContainer_.updateClippingMask( + new print_preview.Size( + pluginWidth - this.plugin_.getVerticalScrollbarThickness(), + pluginHeight - this.plugin_.getHorizontalScrollbarThickness())); + } + }; + + // Export + return { + PreviewArea: PreviewArea + }; +}); diff --git a/chrome/browser/resources/print_preview/print_header.css b/chrome/browser/resources/print_preview/print_header.css new file mode 100644 index 0000000..dca74d3a --- /dev/null +++ b/chrome/browser/resources/print_preview/print_header.css @@ -0,0 +1,15 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +.print-header { + padding-bottom: 10px; + padding-top: 10px; +} + +.print-header-summary { + color: rgb(83, 99, 125); + display: block; + min-height: 30px; +} diff --git a/chrome/browser/resources/print_preview/print_header.html b/chrome/browser/resources/print_preview/print_header.html new file mode 100644 index 0000000..f0c3aeb --- /dev/null +++ b/chrome/browser/resources/print_preview/print_header.html @@ -0,0 +1,10 @@ +<div id="print-header" class="print-header"> + <span class="print-header-summary"></span> + <div class="button-strip"> + <button class="print-header-print-button default" + i18n-content="printButton" + disabled></button> + <button class="print-header-cancel-button" + i18n-content="cancelButton"></button> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/print_header.js b/chrome/browser/resources/print_preview/print_header.js index 6a74d04..2d93429 100644 --- a/chrome/browser/resources/print_preview/print_header.js +++ b/chrome/browser/resources/print_preview/print_header.js @@ -8,176 +8,232 @@ cr.define('print_preview', function() { /** * Creates a PrintHeader object. This object encapsulates all the elements * and logic related to the top part of the left pane in print_preview.html. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read + * information about the document. + * @param {!print_preview.DestinationStore} destinationStore Used to get the + * selected destination. * @constructor + * @extends {print_preview.Component} */ - function PrintHeader() { - this.printButton_ = $('print-button'); - this.cancelButton_ = $('cancel-button'); - this.summary_ = $('print-summary'); - this.printButton_.focus(); - this.addEventListeners_(); - } - - cr.addSingletonGetter(PrintHeader); - - PrintHeader.prototype = { - get printButton() { - return this.printButton_; - }, - - get cancelButton() { - return this.cancelButton_; - }, - - get summary() { - return this.summary_; - }, + function PrintHeader(printTicketStore, destinationStore) { + print_preview.Component.call(this); /** - * Adding event listeners where necessary. Listeners take care of changing - * their behavior depending on the current state, no need to remove them. + * Used to read information about the document. + * @type {!print_preview.PrintTicketStore} * @private */ - addEventListeners_: function() { - this.cancelButton_.onclick = function() { - this.disableCancelButton(); - closePrintPreviewTab(); - }.bind(this); - this.printButton_.onclick = this.onPrintRequested.bind(this); - document.addEventListener(customEvents.UPDATE_SUMMARY, - this.updateSummary_.bind(this)); - document.addEventListener(customEvents.UPDATE_PRINT_BUTTON, - this.updatePrintButton_.bind(this)); - document.addEventListener(customEvents.PDF_GENERATION_ERROR, - this.onPDFGenerationError_.bind(this)); - document.addEventListener(customEvents.PRINTER_CAPABILITIES_UPDATED, - this.onPrinterCapabilitiesUpdated_.bind(this)); - }, + this.printTicketStore_ = printTicketStore; /** - * Enables the cancel button and attaches its keydown event listener. + * Used to get the selected destination. + * @type {!print_preview.DestinationStore} + * @private */ - enableCancelButton: function() { - window.onkeydown = onKeyDown; - this.cancelButton_.disabled = false; - }, + this.destinationStore_ = destinationStore; /** - * Executes when a |customEvents.PDF_GENERATION_ERROR| event occurs. + * Whether the component is enabled. + * @type {boolean} * @private */ - onPDFGenerationError_: function() { - this.printButton_.disabled = true; + this.isEnabled_ = true; + }; + + /** + * Event types dispatched by the print header. + * @enum {string} + */ + PrintHeader.EventType = { + PRINT_BUTTON_CLICK: 'print_preview.PrintHeader.PRINT_BUTTON_CLICK', + CANCEL_BUTTON_CLICK: 'print_preview.PrintHeader.CANCEL_BUTTON_CLICK' + }, + + /** + * CSS classes used by the print header. + * @enum {string} + * @private + */ + PrintHeader.Classes_ = { + CANCEL_BUTTON: 'print-header-cancel-button', + PRINT_BUTTON: 'print-header-print-button', + SUMMARY: 'print-header-summary' + }; + + PrintHeader.prototype = { + __proto__: print_preview.Component.prototype, + + set isEnabled(isEnabled) { + this.isEnabled_ = isEnabled; + this.printButton_.disabled = !isEnabled; + this.cancelButton_.disabled = !isEnabled; }, - /** - * Executes when a |customEvents.PRINTER_CAPABILITIES_UPDATED| event occurs. - * @private - */ - onPrinterCapabilitiesUpdated_: function() { - getSelectedPrinterName() == PRINT_TO_PDF ? - this.printButton.textContent = localStrings.getString('saveButton') : - this.printButton.textContent = localStrings.getString('printButton'); + setErrorMessage: function(message) { + var summaryEl = this.getElement().getElementsByClassName( + PrintHeader.Classes_.SUMMARY)[0]; + summaryEl.innerHTML = ''; + summaryEl.textContent = message; }, - /** - * Disables the cancel button and removes its keydown event listener. - */ - disableCancelButton: function() { - window.onkeydown = null; - this.cancelButton_.disabled = true; + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.printButton_.focus(); + + // User events + this.tracker.add( + this.cancelButton_, 'click', this.onCancelButtonClick_.bind(this)); + this.tracker.add( + this.printButton_, 'click', this.onPrintButtonClick_.bind(this)); + + // Data events. + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onTicketChange_.bind(this)); + this.tracker.add( + this.destinationStore_, + print_preview.DestinationStore.EventType.DESTINATION_SELECT, + this.onDestinationSelect_.bind(this)); }, /** - * Listener executing whenever the print button is clicked or user presses - * the enter button while focus is in the pages field. + * @return {Element} Print button element. + * @private */ - onPrintRequested: function() { - var printToPDF = getSelectedPrinterName() == PRINT_TO_PDF; - if (!printToPDF) { - this.printButton_.classList.add('loading'); - this.cancelButton_.classList.add('loading'); - this.summary_.innerHTML = localStrings.getString('printing'); - } - this.disableCancelButton(); - requestToPrintDocument(); + get printButton_() { + return this.getElement().getElementsByClassName( + PrintHeader.Classes_.PRINT_BUTTON)[0]; }, /** - * Updates the state of |this.printButton_| depending on the user selection. - * The button is enabled only when the following conditions are true. - * 1) The selected page ranges are valid. - * 2) The number of copies is valid (if applicable). + * @return {Element} Cancel button element. * @private */ - updatePrintButton_: function() { - if (showingSystemDialog) - return; - this.printButton_.disabled = !areSettingsValid(); + get cancelButton_() { + return this.getElement().getElementsByClassName( + PrintHeader.Classes_.CANCEL_BUTTON)[0]; }, /** - * Updates |this.summary_| based on the currently selected user options. + * Updates the summary element based on the currently selected user options. * @private */ updateSummary_: function() { - var printToPDF = getSelectedPrinterName() == PRINT_TO_PDF; - var copies = printToPDF ? 1 : copiesSettings.numberOfCopies; - - if ((!printToPDF && !copiesSettings.isValid()) || - !pageSettings.isPageSelectionValid()) { - this.summary_.innerHTML = ''; + var summaryEl = this.getElement().getElementsByClassName( + PrintHeader.Classes_.SUMMARY)[0]; + if (!this.printTicketStore_.isTicketValid()) { + summaryEl.innerHTML = ''; return; } - if (!marginSettings.areMarginSettingsValid()) { - this.summary_.innerHTML = ''; - return; - } - - var pageSet = pageSettings.selectedPagesSet; - var numOfSheets = pageSet.length; - if (numOfSheets == 0) - return; - var summaryLabel = localStrings.getString('printPreviewSheetsLabelSingular'); - var numOfPagesText = ''; var pagesLabel = localStrings.getString('printPreviewPageLabelPlural'); - if (printToPDF) + var saveToPdf = this.destinationStore_.selectedDestination && + this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF; + if (saveToPdf) { summaryLabel = localStrings.getString('printPreviewPageLabelSingular'); + } - if (!printToPDF && - copiesSettings.duplexMode == print_preview.CopiesSettings.LONG_EDGE) { - numOfSheets = Math.ceil(numOfSheets / 2); + var numPages = this.printTicketStore_.getPageNumberSet().size; + var numSheets = numPages; + if (!saveToPdf && this.printTicketStore_.isDuplexEnabled()) { + numSheets = Math.ceil(numPages / 2); } - numOfSheets *= copies; - if (numOfSheets > 1) { - summaryLabel = printToPDF ? pagesLabel : + var copies = this.printTicketStore_.getCopies(); + numSheets *= copies; + numPages *= copies; + + if (numSheets > 1) { + summaryLabel = saveToPdf ? pagesLabel : localStrings.getString('printPreviewSheetsLabelPlural'); } - var html = ''; - if (pageSet.length * copies != numOfSheets) { - numOfPagesText = pageSet.length * copies; + var html; + if (numPages != numSheets) { html = localStrings.getStringF('printPreviewSummaryFormatLong', - '<b>' + numOfSheets + '</b>', + '<b>' + numSheets + '</b>', '<b>' + summaryLabel + '</b>', - numOfPagesText, pagesLabel); + numPages, + pagesLabel); } else { html = localStrings.getStringF('printPreviewSummaryFormatShort', - '<b>' + numOfSheets + '</b>', + '<b>' + numSheets + '</b>', '<b>' + summaryLabel + '</b>'); } // Removing extra spaces from within the string. html = html.replace(/\s{2,}/g, ' '); - this.summary_.innerHTML = html; + summaryEl.innerHTML = html; + }, + + /** + * Called when the print button is clicked. Dispatches a PRINT_DOCUMENT + * common event. + * @private + */ + onPrintButtonClick_: function() { + if (this.destinationStore_.selectedDestination.id != + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { + this.printButton_.classList.add('loading'); + this.cancelButton_.classList.add('loading'); + var summaryEl = this.getElement().getElementsByClassName( + PrintHeader.Classes_.SUMMARY)[0]; + summaryEl.innerHTML = localStrings.getString('printing'); + } + cr.dispatchSimpleEvent(this, PrintHeader.EventType.PRINT_BUTTON_CLICK); + }, + + /** + * Called when the cancel button is clicked. Dispatches a + * CLOSE_PRINT_PREVIEW event. + * @private + */ + onCancelButtonClick_: function() { + cr.dispatchSimpleEvent(this, PrintHeader.EventType.CANCEL_BUTTON_CLICK); + }, + + /** + * Called when a new destination is selected. Updates the text on the print + * button. + * @private + */ + onDestinationSelect_: function() { + if (this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { + this.printButton_.textContent = localStrings.getString('saveButton'); + } else { + this.printButton_.textContent = localStrings.getString('printButton'); + } + }, + + /** + * Called when the print ticket has changed. Disables the print button if + * any of the settings are invalid. + * @private + */ + onTicketChange_: function() { + this.printButton_.disabled = + !this.printTicketStore_.isTicketValid() || + !this.isEnabled_; + this.updateSummary_(); } }; + // Export return { PrintHeader: PrintHeader }; diff --git a/chrome/browser/resources/print_preview/print_preview.css b/chrome/browser/resources/print_preview/print_preview.css index b484c2a..1c91d66 100644 --- a/chrome/browser/resources/print_preview/print_preview.css +++ b/chrome/browser/resources/print_preview/print_preview.css @@ -7,9 +7,9 @@ html { } body { + display: -webkit-box; height: 100%; margin: 0; - overflow: hidden; } /* Header */ @@ -17,20 +17,16 @@ body { header { -webkit-padding-end: 14px; -webkit-padding-start: 16px; + background-color: #F1F1F1; } #print-preview #navbar-container { -webkit-border-end: 1px solid rgb(198, 201, 206); -webkit-box-orient: vertical; -webkit-user-select: none; - background-color: #f1f1f1; - bottom: 0; + background-color: white; display: -webkit-box; - /* We set both left and right for the sake of RTL. */ - left: 0; - position: fixed; - right: 0; - top: 0; + position: relative; width: 310px; z-index: 2; } @@ -45,25 +41,14 @@ header { text-shadow: white 0 1px 2px; } -#print-header { - padding-bottom: 10px; - padding-top: 10px; -} - -#print-summary { - color: rgb(83, 99, 125); - display: block; - min-height: 30px; -} - /* Settings */ #settings { - -webkit-box-flex: 1; -webkit-box-shadow: inset 0 2px 2px rgba(0, 0, 0, .3); background: white; overflow-y: auto; padding-top: 2px; + width: 100%; } .two-column { @@ -87,10 +72,10 @@ header { } .two-column h1 { + -webkit-padding-end: 16px; -webkit-padding-start: 16px; display: table-cell; font-size: 1.1em; - width: 86px; } .two-column.visible h1, @@ -117,19 +102,20 @@ h1 { font-weight: 300; } -.preview-link-button { - -webkit-padding-start: 16px; +#print-preview .navbar-link { + -webkit-margin-start: 16px; + margin-top: 10px; outline: 0; - padding-top: 10px; + padding: 0; text-align: start; text-decoration: none; } -.preview-link-button:hover:not(:disabled) { +#print-preview .navbar-link:hover:not(:disabled) { text-decoration: underline; } -.preview-link-button:disabled { +#print-preview .navbar-link:disabled { color: rgba(0, 0, 0, .5); cursor: default; text-shadow: none; @@ -241,150 +227,12 @@ label { /* Individual settings sections */ -#print-pages-div { - -webkit-box-align: center; - -webkit-box-orient: horizontal; - display: -webkit-box; -} - -#individual-pages { - -webkit-box-flex: 1; - -webkit-margin-start: 5px; - display: block; -} - -#collate-option { - -webkit-padding-start: 16px; - display: inline-block; -} - -#copies { - position: relative; - width: 2.75em; -} - -#copies.invalid { - background: rgb(255, 240, 240); - color: rgb(140, 20, 20); -} - -#increment, -#decrement { - -webkit-padding-end: 0; - -webkit-padding-start: 0; - font-weight: 600; - margin: 0; - min-width: 0; - position: relative; - width: 2em; -} - -#increment:focus, -#decrement:focus, -#copies:focus { - z-index: 1; -} - -#increment { - -webkit-margin-start: -5px; - border-radius: 0; -} - -#decrement { - -webkit-margin-start: -5px; - border-bottom-left-radius: 0; - border-bottom-right-radius: 3px; - border-top-left-radius: 0; - border-top-right-radius: 3px; -} - -html[dir='rtl'] #decrement { - border-bottom-left-radius: 3px; - border-bottom-right-radius: 0; - border-top-left-radius: 3px; - border-top-right-radius: 0; -} - -#system-dialog-link { - -webkit-margin-start: 16px; - margin-top: 10px; - padding: 0; -} - -/* PDF view */ - -#mainview { - -webkit-margin-start: 310px; - -webkit-padding-start: 0; - -webkit-user-select: none; - background-color: #ccc; - bottom: 0; - left: 0; - overflow: hidden; - position: absolute; - right: 0; - top: 0; - z-index: 1; -} - -#pdf-viewer { - /* pluginFadeInTransitionDuration = 200ms */ - -webkit-transition: opacity 200ms linear; - /* pluginFadeInTransitionDelay = overlayFadeOutTransitionDuration = 100ms */ - -webkit-transition-delay: 100ms; - height: 100%; - opacity: 1; - width: 100%; -} - -#pdf-viewer.invisible { - /* pluginFadeOutTransitionDuration = 100ms */ - -webkit-transition: opacity 100ms linear; - /* pluginFadeOutTransitionDelay = 250ms */ - -webkit-transition-delay: 250ms; - opacity: 0; -} - -#no-plugin { - padding: 20px; -} - /* TODO(estade): this should be in a shared location but I'm afraid of the * damage it could do. */ [hidden] { display: none !important; } -#overlay-layer { - -webkit-transition: opacity 200ms linear; - /* overlayFadeInTransitionDelay = pluginFadeOutTransitionDelay + - * pluginFadeOutTransitionDuration = 350ms */ - -webkit-transition-delay: 350ms; - -webkit-user-select: none; - background: #ccc; - height: 100%; - margin: 0; - opacity: 1; - position: absolute; - width: 100%; -} - -#overlay-layer.invisible { - /* overlayFadeOutTransitionDuration = 100ms */ - -webkit-transition: opacity 100ms linear; - opacity: 0; - pointer-events: none; -} - -#messages { - color: #404040; - font-size: 1.1em; - position: relative; - text-align: center; - text-shadow: 0 1px 0 rgba(255, 255, 255, .5); - top: 50%; -} - @-webkit-keyframes dancing-dots-jump { 0% { top: 0; } 55% { top: 0; } @@ -395,28 +243,20 @@ html[dir='rtl'] #decrement { 100% { top: 0; } } -#loading { - -webkit-margin-end: -3px; -} - -.message-with-dots span span { +span.jumping-dots > span { -webkit-animation: dancing-dots-jump 1800ms infinite; padding: 1px; position: relative; } -.message-with-dots span span:nth-child(2) { +span.jumping-dots > span:nth-child(2) { -webkit-animation-delay: 100ms; } -.message-with-dots span span:nth-child(3) { +span.jumping-dots > span:nth-child(3) { -webkit-animation-delay: 300ms; } -#error-action-area { - margin-top: 10px; -} - /* TODO(estade): unfork this code. */ .button-strip { <if expr="not pp_ifdef('toolkit_views')"> @@ -431,3 +271,7 @@ html[dir='rtl'] #decrement { -webkit-margin-start: 4px; display: block; } + +#link-container { + -webkit-box-flex: 1; +} diff --git a/chrome/browser/resources/print_preview/print_preview.html b/chrome/browser/resources/print_preview/print_preview.html index 1090875..58bfce5 100644 --- a/chrome/browser/resources/print_preview/print_preview.html +++ b/chrome/browser/resources/print_preview/print_preview.html @@ -1,92 +1,77 @@ <!DOCTYPE html> <html i18n-values="dir:textdirection;" id="print-preview"> + <head> -<meta charset="utf-8"> -<title i18n-content="title"></title> -<link rel="icon" href="../../../app/theme/print_preview_favicon.png"> + <meta charset="utf-8"/> + <title i18n-content="title"></title> + <link rel="icon" href="../../../app/theme/print_preview_favicon.png"> -<link rel="stylesheet" href="margins.css"> -<link rel="stylesheet" href="print_preview.css"> -<link rel="stylesheet" href="../shared/css/chrome_shared2.css"> -<link rel="stylesheet" href="../shared/css/throbber.css"> + <link rel="stylesheet" href="print_preview.css"/> + <link rel="stylesheet" href="../shared/css/chrome_shared2.css"/> + <link rel="stylesheet" href="../shared/css/throbber.css"/> + <link rel="stylesheet" href="../shared/css/widgets.css"/> + <link rel="stylesheet" href="print_header.css"/> + <link rel="stylesheet" href="settings/copies_settings.css"/> + <link rel="stylesheet" href="settings/page_settings.css"/> + <link rel="stylesheet" href="previewarea/preview_area.css"/> + <link rel="stylesheet" href="previewarea/margin_control_container.css"/> + <link rel="stylesheet" href="previewarea/margin_control.css"/> + <link rel="stylesheet" href="../shared/css/overlay.css"/> -<script src="chrome://resources/js/cr.js"></script> -<script src="chrome://resources/js/event_tracker.js"></script> -<script src="chrome://resources/js/local_strings.js"></script> -<script src="chrome://resources/js/util.js"></script> -<script src="chrome://print/print_preview.js"></script> -<script src="chrome://print/strings.js"></script> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/cr/event_target.js"></script> + <script src="chrome://resources/js/event_tracker.js"></script> + <script src="chrome://resources/js/local_strings.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="chrome://print/print_preview.js"></script> + <script src="chrome://print/strings.js"></script> + <script src="chrome://resources/js/i18n_template.js"></script> </head> + <body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> <div id="navbar-container"> <header> <h1 id="navbar-content-title" i18n-content="title"></h1> - <div id="print-header"> - <span id="print-summary"></span> - <div class="button-strip"> - <button id="print-button" i18n-content="printButton" class="default"> - </button> - <button id="cancel-button" i18n-content="cancelButton"></button> - </div> - </div> + <include src="print_header.html"/> </header> <div id="settings"> - <div id="destination-option" class="two-column visible"> - <h1 i18n-content="destinationLabel"></h1> - <div class="right-column"> - <select id="printer-list"></select> - </div> - </div> - <include src="page_settings.html"></include> - <include src="copies_settings.html"></include> - <include src="layout_settings.html"></include> - <include src="color_settings.html"></include> - <include src="margin_settings.html"></include> - <include src="more_options.html"></include> + <include src="settings/destination_settings.html"/> + <include src="settings/page_settings.html"/> + <include src="settings/copies_settings.html"/> + <include src="settings/layout_settings.html"/> + <include src="settings/color_settings.html"/> + <include src="settings/margin_settings.html"/> + <include src="settings/other_options_settings.html"/> + </div> + <div id="link-container"> <div> - <if expr="pp_ifdef('chromeos')"> - <button id="system-dialog-link" - class="link-button preview-link-button" - i18n-content="cloudPrintDialogOption"></button> - </if> - <if expr="not pp_ifdef('chromeos')"> - <button id="system-dialog-link" - class="link-button preview-link-button" - i18n-content="systemDialogOption"></button> - </if> - <div id="system-dialog-throbber" class="throbber" hidden></div> + <button id="cloud-print-dialog-link" + class="link-button navbar-link" + style="display: none;" + i18n-content="cloudPrintDialogOption"></button> + <button id="system-dialog-link" + class="link-button navbar-link" + style="display: none;" + i18n-content="systemDialogOption"></button> + <div id="dialog-throbber" + style="display: none;" + class="throbber"></div> </div> - <if expr="is_macosx"> - <div> - <button id="open-pdf-in-preview-link" - class="link-button preview-link-button" - i18n-content="openPdfInPreviewOption"></button> - <div id="open-preview-app-throbber" class="throbber" hidden></div> - </div> - </if> - </div> - </div> - <div id="mainview"> - <div id="overlay-layer" class="invisible"> - <div id="messages"> - <div id="dancing-dots-text" hidden> - <span id="loading"></span> - <span><span>.</span><span>.</span><span>.</span></span> - </div> - <div id="custom-message" hidden></div> - <div id="custom-message-with-dots" - class="message-with-dots" hidden></div> - <div id="error-action-area"> - <button id="error-button" hidden></button> - <div id="native-print-dialog-throbber" class="throbber" hidden></div> - </div> + <div> + <button id="open-pdf-in-preview-link" + class="link-button navbar-link" + style="display: none;" + i18n-content="openPdfInPreviewOption"></button> + <div id="open-preview-app-throbber" + style="display: none;" + class="throbber"></div> </div> </div> </div> - <object id="dummy-viewer" - type="application/x-google-chrome-print-preview-pdf" - data="chrome://print/dummy.pdf"></object> - <script src="chrome://resources/js/i18n_template.js"></script> - <script src="chrome://resources/js/i18n_process.js"></script> + <include src="previewarea/preview_area.html"/> + + <!-- HTML Templates --> + <include src="previewarea/margin_control.html"/> </body> + </html> diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js index f47cb29..ed78cfc 100644 --- a/chrome/browser/resources/print_preview/print_preview.js +++ b/chrome/browser/resources/print_preview/print_preview.js @@ -2,1267 +2,916 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// require: cr/ui/print_preview_cloud.js +// TODO(rltoscano): Might be a problem where might be in fetching destinations +// state, then to file selection state, then cancel, which results in the +// fetching destinations state being lost. -var localStrings = new LocalStrings(); - -// If useCloudPrint is true we attempt to connect to cloud print -// and populate the list of printers with cloud print printers. -var useCloudPrint = false; - -// Store the last selected printer index. -var lastSelectedPrinterIndex = 0; - -// Used to disable some printing options when the preview is not modifiable. -var previewModifiable = false; - -// Used to identify whether the printing frame has specific page size style. -var hasPageSizeStyle = false; - -// Destination list special value constants. - -/** @const */ var MANAGE_CLOUD_PRINTERS = 'manageCloudPrinters'; -/** @const */ var MANAGE_LOCAL_PRINTERS = 'manageLocalPrinters'; -/** @const */ var SIGN_IN = 'signIn'; -/** @const */ var PRINT_TO_PDF = 'Save as PDF'; -/** @const */ var PRINT_WITH_CLOUD_PRINT = 'printWithCloudPrint'; - -// State of the print preview settings. -var printSettings = new PrintSettings(); - -// Print ready data index. -/** @const */ var PRINT_READY_DATA_INDEX = -1; - -// The name of the default or last used printer. -var defaultOrLastUsedPrinterName = ''; - -// True when a pending print preview request exists. -var hasPendingPreviewRequest = false; - -// True when the first page is loaded in the plugin. -var isFirstPageLoaded = false; - -// The ID of the last preview request. -var lastPreviewRequestID = -1; - -// The ID of the initial preview request. -var initialPreviewRequestID = -1; - -// True when a pending print file request exists. -var hasPendingPrintDocumentRequest = false; +// TODO(rltoscano): Move data/* into print_preview.data namespace -// True when the complete metafile for the previewed doc is ready. -var isPrintReadyMetafileReady = false; +// TODO(rltoscano): Handle case where cloud print is initial destination, but +// cloud print is not enabled. -// True when preview tab is hidden. -var isTabHidden = false; - -// True in kiosk mode where print preview can print automatically without -// user intervention. See http://crbug.com/31395. -var printAutomaticallyInKioskMode = false; - -// @type {print_preview.PrintHeader} Holds the print and cancel buttons. -var printHeader; - -// @type {print_preview.PageSettings} Holds all the pages related settings. -var pageSettings; +var localStrings = new LocalStrings(); -// @type {print_preview.CopiesSettings} Holds all the copies related settings. -var copiesSettings; - -// @type {print_preview.LayoutSettings} Holds all the layout related settings. -var layoutSettings; - -// @type {print_preview.MarginSettings} Holds all the margin related settings. -var marginSettings; - -// @type {print_preview.HeaderFooterSettings} Holds all the header footer -// related settings. -var headerFooterSettings; - -// @type {print_preview.FitToPageSettings} Holds all the fit to page related -// settings. -var fitToPageSettings; - -// @type {print_preview.MoreOptions} Holds the more options implementation. -var moreOptions; - -// @type {print_preview.ColorSettings} Holds all the color related settings. -var colorSettings; - -// @type {print_preview.PreviewArea} Holds information related to the preview -// area (on the right). -var previewArea; - -// True if the user has click 'Advanced...' in order to open the system print -// dialog. -var showingSystemDialog = false; - -// True if the user has clicked 'Open PDF in Preview' option. -var previewAppRequested = false; - -// The range of options in the printer dropdown controlled by cloud print. -var firstCloudPrintOptionPos = 0; -var lastCloudPrintOptionPos = firstCloudPrintOptionPos; - -// Store the current previewUid. -var currentPreviewUid = ''; - -// True if we need to generate draft preview data. -var generateDraftData = true; - -// The last element clicked with the mouse. -var lastClickedElement = null; - -// A dictionary of cloud printers that have been added to the printer -// dropdown. -var addedCloudPrinters = {}; - -// The maximum number of cloud printers to allow in the dropdown. -/** @const */ var maxCloudPrinters = 10; - -/** @const */ var MIN_REQUEST_ID = 0; -/** @const */ var MAX_REQUEST_ID = 32000; - -// Names of all the custom events used. -var customEvents = { - // Fired when the header footer option visibility changed. - HEADER_FOOTER_VISIBILITY_CHANGED: 'headerFooterVisibilityChanged', - // Fired when the mouse moves while a margin line is being dragged. - MARGIN_LINE_DRAG: 'marginLineDrag', - // Fired when a mousedown event occurs on a margin line. - MARGIN_LINE_MOUSE_DOWN: 'marginLineMouseDown', - // Fired when a margin textbox gains focus. - MARGIN_TEXTBOX_FOCUSED: 'marginTextboxFocused', - // Fired when a new preview might be needed because of margin changes. - MARGINS_MAY_HAVE_CHANGED: 'marginsMayHaveChanged', - // Fired when a pdf generation related error occurs. - PDF_GENERATION_ERROR: 'PDFGenerationError', - // Fired once the first page of the pdf document is loaded in the plugin. - PDF_LOADED: 'PDFLoaded', - // Fired when the selected printer capabilities change. - PRINTER_CAPABILITIES_UPDATED: 'printerCapabilitiesUpdated', - // Fired when the destination printer is changed. - PRINTER_SELECTION_CHANGED: 'printerSelectionChanged', - // Fired when the print button needs to be updated. - UPDATE_PRINT_BUTTON: 'updatePrintButton', - // Fired when the print summary needs to be updated. - UPDATE_SUMMARY: 'updateSummary' -}; - -/** - * Window onload handler, sets up the page and starts print preview by getting - * the printer list. - */ -function onLoad() { - initialPreviewRequestID = randomInteger(MIN_REQUEST_ID, MAX_REQUEST_ID); - lastPreviewRequestID = initialPreviewRequestID; - - previewArea = print_preview.PreviewArea.getInstance(); - printHeader = print_preview.PrintHeader.getInstance(); - document.addEventListener(customEvents.PDF_GENERATION_ERROR, - cancelPendingPrintRequest); - document.addEventListener('click', setLastClickedElement); - - if (!checkCompatiblePluginExists()) { - disableInputElementsInSidebar(); - $('cancel-button').focus(); - previewArea.displayErrorMessageWithButtonAndNotify( - localStrings.getString('noPlugin'), - localStrings.getString('launchNativeDialog'), - launchNativePrintDialog); - $('mainview').parentElement.removeChild($('dummy-viewer')); - return; - } - - $('system-dialog-link').addEventListener('click', onSystemDialogLinkClicked); - if (cr.isMac) { - $('open-pdf-in-preview-link').addEventListener( - 'click', onOpenPdfInPreviewLinkClicked); - } - $('mainview').parentElement.removeChild($('dummy-viewer')); - - $('printer-list').disabled = true; - - pageSettings = print_preview.PageSettings.getInstance(); - copiesSettings = print_preview.CopiesSettings.getInstance(); - layoutSettings = print_preview.LayoutSettings.getInstance(); - marginSettings = print_preview.MarginSettings.getInstance(); - headerFooterSettings = print_preview.HeaderFooterSettings.getInstance(); - fitToPageSettings = print_preview.FitToPageSettings.getInstance(); - moreOptions = print_preview.MoreOptions.getInstance(); - colorSettings = print_preview.ColorSettings.getInstance(); - $('printer-list').onchange = updateControlsWithSelectedPrinterCapabilities; - - previewArea.showLoadingAnimation(); - chrome.send('getInitialSettings'); -} - -/** - * @param {object} initialSettings An object containing all the initial - * settings. - */ -function setInitialSettings(initialSettings) { - setInitiatorTabTitle(initialSettings['initiatorTabTitle']); - previewModifiable = initialSettings['previewModifiable']; - if (previewModifiable) { - print_preview.MarginSettings.setNumberFormatAndMeasurementSystem( - initialSettings['numberFormat'], - initialSettings['measurementSystem']); - marginSettings.setLastUsedMargins(initialSettings); - } - printAutomaticallyInKioskMode = - initialSettings['printAutomaticallyInKioskMode']; - headerFooterSettings.setChecked(initialSettings['headerFooterEnabled']); - copiesSettings.previousDuplexMode = initialSettings['duplex']; - setDefaultPrinter(initialSettings['printerName'], - initialSettings['cloudPrintData']); -} - -/** - * Disables the input elements in the sidebar. - */ -function disableInputElementsInSidebar() { - var els = $('navbar-container').querySelectorAll('input, button, select'); - for (var i = 0; i < els.length; i++) { - if (els[i] == printHeader.cancelButton) - continue; - els[i].disabled = true; - } -} - -/** - * Enables the input elements in the sidebar. - */ -function enableInputElementsInSidebar() { - var els = $('navbar-container').querySelectorAll('input, button, select'); - for (var i = 0; i < els.length; i++) - els[i].disabled = false; -} - -/** - * Keep track of the last element to receive a click. - * @param {Event} e The click event. - */ -function setLastClickedElement(e) { - lastClickedElement = e.target; -} - -/** - * Disables the controls in the sidebar, shows the throbber and instructs the - * backend to open the native print dialog. - */ -function onSystemDialogLinkClicked() { - if (showingSystemDialog) - return; - showingSystemDialog = true; - disableInputElementsInSidebar(); - printHeader.disableCancelButton(); - $('system-dialog-throbber').hidden = false; - chrome.send('showSystemDialog'); -} - -/** - * Disables the controls in the sidebar, shows the throbber and instructs the - * backend to open the pdf in native preview app. This is only for Mac. - */ -function onOpenPdfInPreviewLinkClicked() { - if (previewAppRequested) - return; - previewAppRequested = true; - disableInputElementsInSidebar(); - $('open-preview-app-throbber').hidden = false; - printHeader.disableCancelButton(); - requestToPrintDocument(); -} - -/** - * Similar to onSystemDialogLinkClicked(), but specific to the - * 'Launch native print dialog' UI. - */ -function launchNativePrintDialog() { - if (showingSystemDialog) - return; - showingSystemDialog = true; - previewArea.errorButton.disabled = true; - printHeader.disableCancelButton(); - $('native-print-dialog-throbber').hidden = false; - chrome.send('showSystemDialog'); -} - -/** - * Notifies listeners of |customEvents.PRINTER_SELECTION_CHANGED| event about - * the current selected printer. - */ -function dispatchPrinterSelectionChangedEvent() { - var customEvent = cr.Event(customEvents.PRINTER_SELECTION_CHANGED); - customEvent.selectedPrinter = getSelectedPrinterName(); - document.dispatchEvent(customEvent); -} - -/** - * Gets the selected printer capabilities and updates the controls accordingly. - */ -function updateControlsWithSelectedPrinterCapabilities() { - var printerList = $('printer-list'); - var selectedIndex = printerList.selectedIndex; - if (selectedIndex < 0) - return; - if (cr.isMac) - $('open-pdf-in-preview-link').disabled = false; - - var skip_refresh = false; - var selectedPrinterChanged = true; - var selectedValue = printerList.options[selectedIndex].value; - if (cloudprint.isCloudPrint(printerList.options[selectedIndex])) { - cloudprint.updatePrinterCaps(printerList.options[selectedIndex], - doUpdateCloudPrinterCapabilities); - skip_refresh = true; - } else if (selectedValue == SIGN_IN || - selectedValue == MANAGE_CLOUD_PRINTERS || - selectedValue == MANAGE_LOCAL_PRINTERS) { - printerList.selectedIndex = lastSelectedPrinterIndex; - chrome.send(selectedValue); - skip_refresh = true; - selectedPrinterChanged = false; - } else if (selectedValue == PRINT_TO_PDF || - selectedValue == PRINT_WITH_CLOUD_PRINT) { - updateWithPrinterCapabilities({ - 'disableColorOption': true, - 'setColorAsDefault': true, - 'setDuplexAsDefault': false, - 'printerColorModelForColor': print_preview.ColorSettings.COLOR, - 'printerDefaultDuplexValue': copiesSettings.UNKNOWN_DUPLEX_MODE, - 'disableCopiesOption': true}); - if (cr.isChromeOS && selectedValue == PRINT_WITH_CLOUD_PRINT) - requestToPrintDocument(); - } else { - // This message will call back to 'updateWithPrinterCapabilities' - // function. - chrome.send('getPrinterCapabilities', [selectedValue]); - } - if (selectedPrinterChanged) - dispatchPrinterSelectionChangedEvent(); - - if (!skip_refresh) { - lastSelectedPrinterIndex = selectedIndex; - - // Regenerate the preview data based on selected printer settings. - // Do not reset the margins if no preview request has been made. - var resetMargins = lastPreviewRequestID != initialPreviewRequestID; - setDefaultValuesAndRegeneratePreview(resetMargins); - } -} - -/** - * Helper function to do the actual work of updating cloud printer - * capabilities. - * @param {Object} printer The printer object to set capabilities for. - */ -function doUpdateCloudPrinterCapabilities(printer) { - var settings = {'disableColorOption': !cloudprint.supportsColor(printer), - 'setColorAsDefault': cloudprint.colorIsDefault(printer), - 'disableCopiesOption': true, - 'disableLandscapeOption': true}; - updateWithPrinterCapabilities(settings); - var printerList = $('printer-list'); - var selectedIndex = printerList.selectedIndex; - lastSelectedPrinterIndex = selectedIndex; - - // Regenerate the preview data based on selected printer settings. - // Do not reset the margins if no preview request has been made. - var resetMargins = lastPreviewRequestID != initialPreviewRequestID; - setDefaultValuesAndRegeneratePreview(resetMargins); -} - -/** - * Notifies listeners of |customEvents.PRINTER_CAPABILITIES_UPDATED| about the - * capabilities of the currently selected printer. It is called from C++ too. - * @param {Object} settingInfo printer setting information. - */ -function updateWithPrinterCapabilities(settingInfo) { - var customEvent = new cr.Event(customEvents.PRINTER_CAPABILITIES_UPDATED); - customEvent.printerCapabilities = settingInfo; - document.dispatchEvent(customEvent); -} - -/** - * Reloads the printer list. - */ -function reloadPrintersList() { - $('printer-list').length = 0; - firstCloudPrintOptionPos = 0; - lastCloudPrintOptionPos = 0; - chrome.send('getPrinters'); -} - -/** - * Turn on the integration of Cloud Print. - * @param {string} cloudPrintURL The URL to use for cloud print servers. - */ -function setUseCloudPrint(cloudPrintURL) { - useCloudPrint = true; - cloudprint.setBaseURL(cloudPrintURL); -} - -/** - * Take the PDF data handed to us and submit it to the cloud, closing the print - * preview tab once the upload is successful. - * @param {string} data Data to send as the print job. - */ -function printToCloud(data) { - cloudprint.printToCloud(data, finishedCloudPrinting); -} - -/** - * Cloud print upload of the PDF file is finished, time to close the dialog. - */ -function finishedCloudPrinting() { - closePrintPreviewTab(); -} - -/** - * Updates the fit to page option state based on the print scaling option of - * source pdf. PDF's have an option to enable/disable print scaling. When we - * find out that the print scaling option is disabled for the source pdf, we - * uncheck the fit to page checkbox. This function is called from C++ code. - */ -function printScalingDisabledForSourcePDF() { - fitToPageSettings.onPrintScalingDisabled(); -} - -/** - * Checks whether the specified settings are valid. - * - * @return {boolean} true if settings are valid, false if not. - */ -function areSettingsValid() { - var selectedPrinter = getSelectedPrinterName(); - return pageSettings.isPageSelectionValid() && - marginSettings.areMarginSettingsValid() && - (copiesSettings.isValid() || selectedPrinter == PRINT_TO_PDF || - selectedPrinter == PRINT_WITH_CLOUD_PRINT); -} - -/** - * Creates an object based on the values in the printer settings. - * - * @return {Object} Object containing print job settings. - */ -function getSettings() { - var deviceName = getSelectedPrinterName(); - var printToPDF = deviceName == PRINT_TO_PDF; - var printWithCloudPrint = deviceName == PRINT_WITH_CLOUD_PRINT; - - var settings = - {'deviceName': deviceName, - 'pageRange': pageSettings.selectedPageRanges, - 'duplex': copiesSettings.duplexMode, - 'copies': copiesSettings.numberOfCopies, - 'collate': copiesSettings.isCollated(), - 'landscape': layoutSettings.isLandscape(), - 'color': colorSettings.colorMode, - 'printToPDF': printToPDF, - 'printWithCloudPrint': printWithCloudPrint, - 'isFirstRequest' : false, - 'headerFooterEnabled': headerFooterSettings.hasHeaderFooter(), - 'marginsType': marginSettings.selectedMarginsValue, - 'requestID': -1, - 'generateDraftData': generateDraftData, - 'fitToPageEnabled': fitToPageSettings.hasFitToPage(), - 'previewModifiable': previewModifiable}; - - if (marginSettings.isCustomMarginsSelected()) - settings['marginsCustom'] = marginSettings.customMargins; - - var printerList = $('printer-list'); - var selectedPrinter = printerList.selectedIndex; - if (cloudprint.isCloudPrint(printerList.options[selectedPrinter])) { - settings['cloudPrintID'] = - printerList.options[selectedPrinter].value; - } - return settings; -} - -/** - * Creates an object based on the values in the printer settings. - * Note: |lastPreviewRequestID| is being modified every time this function is - * called. Only call this function when a preview request is actually sent, - * otherwise (for example when debugging) call getSettings(). - * - * @return {Object} Object containing print job settings. - */ -function getSettingsWithRequestID() { - var settings = getSettings(); - settings.requestID = generatePreviewRequestID(); - settings.isFirstRequest = isFirstPreviewRequest(); - return settings; -} - -/** - * @return {number} The next unused preview request id. - */ -function generatePreviewRequestID() { - return ++lastPreviewRequestID; -} - -/** - * @return {boolean} True iff a preview has been requested. - */ -function hasRequestedPreview() { - return lastPreviewRequestID != initialPreviewRequestID; -} - -/** - * @return {boolean} True if |lastPreviewRequestID| corresponds to the initial - * preview request. - */ -function isFirstPreviewRequest() { - return lastPreviewRequestID == initialPreviewRequestID + 1; -} - -/** - * Checks if |previewResponseId| matches |lastPreviewRequestId|. Used to ignore - * obsolete preview data responses. - * @param {number} previewResponseId The id to check. - * @return {boolean} True if previewResponseId reffers to the expected response. - */ -function isExpectedPreviewResponse(previewResponseId) { - return lastPreviewRequestID == previewResponseId; -} - -/** - * Returns the name of the selected printer or the empty string if no - * printer is selected. - * @return {string} The name of the currently selected printer. - */ -function getSelectedPrinterName() { - var printerList = $('printer-list'); - var selectedPrinter = printerList.selectedIndex; - if (selectedPrinter < 0) - return ''; - return printerList.options[selectedPrinter].value; -} - -/** - * Asks the browser to print the preview PDF based on current print - * settings. If the preview is still loading, printPendingFile() will get - * called once the preview loads. - */ -function requestToPrintDocument() { - hasPendingPrintDocumentRequest = !isPrintReadyMetafileReady; - var selectedPrinterName = getSelectedPrinterName(); - var printToPDF = selectedPrinterName == PRINT_TO_PDF; - var printWithCloudPrint = selectedPrinterName == PRINT_WITH_CLOUD_PRINT; - if (hasPendingPrintDocumentRequest) { - if (previewAppRequested) { - previewArea.showCustomMessage( +<include src="component.js"/> + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Container class for Chromium's print preview. + * @constructor + * @extends {print_preview.Component} + */ + function PrintPreview() { + print_preview.Component.call(this); + + /** + * Used to communicate with Chromium's print system. + * @type {!print_preview.NativeLayer} + * @private + */ + this.nativeLayer_ = new print_preview.NativeLayer(); + + /** + * Data store which holds print destinations. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = new print_preview.DestinationStore(); + + /** + * Storage of the print ticket used to create the print job. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = new print_preview.PrintTicketStore( + this.destinationStore_); + + /** + * Holds the print and cancel buttons and renders some document statistics. + * @type {!print_preview.PrintHeader} + * @private + */ + this.printHeader_ = new print_preview.PrintHeader( + this.printTicketStore_, this.destinationStore_); + this.addChild(this.printHeader_); + + /** + * Component that renders the print destination. + * @type {!print_preview.DestinationSettings} + * @private + */ + this.destinationSettings_ = new print_preview.DestinationSettings( + this.destinationStore_); + this.addChild(this.destinationSettings_); + + /** + * Component that renders UI for entering in page range. + * @type {!print_preview.PageSettings} + * @private + */ + this.pageSettings_ = new print_preview.PageSettings(this.printTicketStore_); + this.addChild(this.pageSettings_); + + /** + * Component that renders the copies settings. + * @type {!print_preview.CopiesSettings} + * @private + */ + this.copiesSettings_ = new print_preview.CopiesSettings( + this.printTicketStore_); + this.addChild(this.copiesSettings_); + + /** + * Component that renders the layout settings. + * @type {!print_preview.LayoutSettings} + * @private + */ + this.layoutSettings_ = new print_preview.LayoutSettings( + this.printTicketStore_); + this.addChild(this.layoutSettings_); + + /** + * Component that renders the color options. + * @type {!print_preview.ColorSettings} + * @private + */ + this.colorSettings_ = new print_preview.ColorSettings( + this.printTicketStore_); + this.addChild(this.colorSettings_); + + /** + * Component that renders a select box for choosing margin settings. + * @type {!print_preview.MarginSettings} + * @private + */ + this.marginSettings_ = new print_preview.MarginSettings( + this.printTicketStore_); + this.addChild(this.marginSettings_); + + /** + * Component that renders miscellaneous print options. + * @type {!print_preview.OtherOptionsSettings} + * @private + */ + this.otherOptionsSettings_ = new print_preview.OtherOptionsSettings( + this.printTicketStore_); + this.addChild(this.otherOptionsSettings_); + + /** + * Area of the UI that holds the print preview. + * @type {!print_preview.PreviewArea} + * @private + */ + this.previewArea_ = new print_preview.PreviewArea( + this.destinationStore_, this.printTicketStore_, this.nativeLayer_); + this.addChild(this.previewArea_); + + /** + * Interface to the Google Cloud Print API. Null if Google Cloud Print + * integration is disabled. + * @type {cloudprint.CloudPrintInterface} + * @private + */ + this.cloudPrintInterface_ = null; + + /** + * Whether in kiosk mode where print preview can print automatically without + * user intervention. See http://crbug.com/31395. Print will start when + * both the print ticket has been initialized, and an initial printer has + * been selected. + * @type {boolean} + * @private + */ + this.isInKioskAutoPrintMode_ = false; + + /** + * State of the print preview UI. + * @type {print_preview.PrintPreview.UiState_} + * @private + */ + this.uiState_ = PrintPreview.UiState_.INITIALIZING; + + /** + * Current state of fetching destinations. + * @type {print_preview.PrintPreview.FetchState_} + * @private + */ + this.fetchState_ = PrintPreview.FetchState_.READY; + + /** + * Whether document preview generation is in progress. + * @type {boolean} + * @private + */ + this.isPreviewGenerationInProgress_ = true; + + this.tracker.add(window, 'DOMContentLoaded', this.onWindowLoad_.bind(this)); + }; + + /** + * States of the print preview. + * @enum {string} + * @private + */ + PrintPreview.UiState_ = { + INITIALIZING: 'initializing', + READY: 'ready', + OPENING_PDF_PREVIEW: 'opening-pdf-preview', + OPENING_NATIVE_PRINT_DIALOG: 'opening-native-print-dialog', + PRINTING: 'printing', + FILE_SELECTION: 'file-selection', + CLOSING: 'closing', + ERROR: 'error' + }; + + /** + * Bitfield of the states of fetching destinations. + * @enum {number} + * @private + */ + PrintPreview.FetchState_ = { + READY: 1, + LOCAL_DESTINATIONS: 2, + RECENT_CLOUD_DESTINATIONS: 4, + ALL_CLOUD_DESTINATIONS: 8 + }; + + PrintPreview.prototype = { + __proto__: print_preview.Component.prototype, + + /** @override */ + decorateInternal: function() { + this.printHeader_.decorate($('print-header')); + this.destinationSettings_.decorate($('destination-settings')); + this.pageSettings_.decorate($('page-settings')); + this.copiesSettings_.decorate($('copies-settings')); + this.layoutSettings_.decorate($('layout-settings')); + this.colorSettings_.decorate($('color-settings')); + this.marginSettings_.decorate($('margin-settings')); + this.otherOptionsSettings_.decorate($('other-options-settings')); + this.previewArea_.decorate($('preview-area')); + + setIsVisible($('cloud-print-dialog-link'), cr.isChromeOS); + setIsVisible($('system-dialog-link'), !cr.isChromeOS); + setIsVisible($('open-pdf-in-preview-link'), cr.isMac); + }, + + /** @override */ + enterDocument: function() { + // Native layer events. + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.INITIAL_SETTINGS_SET, + this.onInitialSettingsSet_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.CLOUD_PRINT_ENABLE, + this.onCloudPrintEnable_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET, + this.onLocalDestinationsSet_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.CAPABILITIES_SET, + this.onLocalDestinationCapabilitiesSet_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD, + this.onDestinationsReload_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.PRINT_TO_CLOUD, + this.onPrintToCloud_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.FILE_SELECTION_CANCEL, + this.onFileSelectionCancel_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.FILE_SELECTION_COMPLETE, + this.onFileSelectionComplete_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.SETTINGS_INVALID, + this.onSettingsInvalid_.bind(this)); + this.tracker.add( + this.nativeLayer_, + print_preview.NativeLayer.EventType.DISABLE_SCALING, + this.onDisableScaling_.bind(this)); + + this.tracker.add( + $('system-dialog-link'), + 'click', + this.openSystemPrintDialog_.bind(this)); + this.tracker.add( + $('cloud-print-dialog-link'), + 'click', + this.openSystemPrintDialog_.bind(this)); + this.tracker.add( + $('open-pdf-in-preview-link'), + 'click', + this.onOpenPdfInPreviewLinkClick_.bind(this)); + + this.tracker.add( + this.previewArea_, + print_preview.PreviewArea.EventType.PREVIEW_GENERATION_IN_PROGRESS, + this.onPreviewGenerationInProgress_.bind(this)); + this.tracker.add( + this.previewArea_, + print_preview.PreviewArea.EventType.PREVIEW_GENERATION_DONE, + this.onPreviewGenerationDone_.bind(this)); + this.tracker.add( + this.previewArea_, + print_preview.PreviewArea.EventType.PREVIEW_GENERATION_FAIL, + this.onPreviewGenerationFail_.bind(this)); + this.tracker.add( + this.previewArea_, + print_preview.PreviewArea.EventType.OPEN_SYSTEM_DIALOG_CLICK, + this.openSystemPrintDialog_.bind(this)); + + this.tracker.add( + this.destinationStore_, + print_preview.DestinationStore.EventType.DESTINATION_SELECT, + this.onDestinationSelect_.bind(this)); + + this.tracker.add( + this.printHeader_, + print_preview.PrintHeader.EventType.PRINT_BUTTON_CLICK, + this.onPrintButtonClick_.bind(this)); + this.tracker.add( + this.printHeader_, + print_preview.PrintHeader.EventType.CANCEL_BUTTON_CLICK, + this.onCancelButtonClick_.bind(this)); + + this.tracker.add( + this.destinationSettings_, + print_preview.DestinationSettings.EventType.MANAGE_PRINTERS_SELECT, + this.onManagePrinters_.bind(this)); + + this.tracker.add(window, 'keydown', this.onKeyDown_.bind(this)); + }, + + /** + * Sets whether the controls in the print preview are enabled. + * @param {boolean} isEnabled Whether the controls in the print preview are + * enabled. + * @private + */ + setIsEnabled_: function(isEnabled) { + $('system-dialog-link').disabled = !isEnabled; + $('cloud-print-dialog-link').disabled = !isEnabled; + $('open-pdf-in-preview-link').disabled = !isEnabled; + this.printHeader_.isEnabled = isEnabled; + this.destinationSettings_.isEnabled = isEnabled; + this.pageSettings_.isEnabled = isEnabled; + this.copiesSettings_.isEnabled = isEnabled; + this.layoutSettings_.isEnabled = isEnabled; + this.colorSettings_.isEnabled = isEnabled; + this.marginSettings_.isEnabled = isEnabled; + this.otherOptionsSettings_.isEnabled = isEnabled; + }, + + /** + * Creates a local PDF print destination. + * @return {!print_preview.Destination} Created print destination. + * @private + */ + createLocalPdfPrintDestination_: function() { + var dest = new print_preview.Destination( + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF, + localStrings.getString('printToPDF'), + false /*isRecent*/, + true /*isLocal*/); + dest.capabilities = new print_preview.ChromiumCapabilities( + false /*hasCopiesCapability*/, + '1' /*defaultCopiesStr*/, + false /*hasCollateCapability*/, + false /*defaultIsCollateEnabled*/, + false /*hasDuplexCapability*/, + false /*defaultIsDuplexEnabled*/, + true /*hasOrientationCapability*/, + false /*defaultIsLandscapeEnabled*/, + true /*hasColorCapability*/, + true /*defaultIsColorEnabled*/); + return dest; + }, + + /** + * Creates a new "Print with Cloud Print" print destination. NOTE: this + * destination will appear as "Search for additional printers..." on + * Chrome OS. + * @return {!print_preview.Destination} Created print destination. + * @private + */ + createPrintWithCloudPrintDestination_: function() { + var dest = new print_preview.Destination( + print_preview.Destination.GooglePromotedId.PRINT_WITH_CLOUD_PRINT, + localStrings.getString('printWithCloudPrint'), + false /*isRecent*/, + false /*isLocal*/); + dest.capabilities = new print_preview.ChromiumCapabilities( + false /*hasCopiesCapability*/, + '1' /*defaultCopiesStr*/, + false /*hasCollateCapability*/, + false /*defaultIsCollateEnabled*/, + false /*hasDuplexCapability*/, + false /*defaultIsDuplexEnabled*/, + true /*hasOrientationCapability*/, + false /*defaultIsLandscapeEnabled*/, + true /*hasColorCapability*/, + true /*defaultIsColorEnabled*/); + return dest; + }, + + /** + * Prints the document or launches a pdf preview on the local system. + * @param {boolean} isPdfPreview Whether to launch the pdf preview. + * @private + */ + printDocumentOrOpenPdfPreview_: function(isPdfPreview) { + assert(this.uiState_ == PrintPreview.UiState_.READY, + 'Print document request received when not in ready state: ' + + this.uiState_); + if (isPdfPreview) { + this.uiState_ = PrintPreview.UiState_.OPENING_PDF_PREVIEW; + } else if (this.destinationStore_.selectedDestination.id == + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { + this.uiState_ = PrintPreview.UiState_.FILE_SELECTION; + } else { + this.uiState_ = PrintPreview.UiState_.PRINTING; + } + this.setIsEnabled_(false); + if (this.printIfReady_() && + ((this.destinationStore_.selectedDestination.isLocal && + this.destinationStore_.selectedDestination.id != + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) || + this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW)) { + // Hide the dialog for now. The actual print command will be issued when + // the preview generation is done. + this.nativeLayer_.startHideDialog(); + } + }, + + /** + * Attempts to print if needed and if ready. + * @return {boolean} Whether a print request was issued. + * @private + */ + printIfReady_: function() { + if ((this.uiState_ == PrintPreview.UiState_.PRINTING || + this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW || + this.uiState_ == PrintPreview.UiState_.FILE_SELECTION || + this.isInKioskAutoPrintMode_) && + !this.isPreviewGenerationInProgress_ && + this.destinationStore_.selectedDestination && + this.destinationStore_.selectedDestination.capabilities) { + assert(this.printTicketStore_.isTicketValid(), + 'Trying to print with invalid ticket'); + this.nativeLayer_.startSaveDestinationAndTicket( + this.destinationStore_.selectedDestination, + this.printTicketStore_); + this.nativeLayer_.startPrint( + this.destinationStore_.selectedDestination, + this.printTicketStore_, + this.cloudPrintInterface_, + this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW); + return true; + } else { + return false; + } + }, + + /** + * Closes the print preview. + * @private + */ + close_: function() { + this.exitDocument(); + this.uiState_ = PrintPreview.UiState_.CLOSING; + this.nativeLayer_.startCloseDialog(); + }, + + /** + * Opens the native system print dialog after disabling all controls. + * @private + */ + openSystemPrintDialog_: function() { + assert(this.uiState_ == PrintPreview.UiState_.READY, + 'Opening system dialog when not in ready state: ' + this.uiState_); + setIsVisible($('dialog-throbber'), true); + this.setIsEnabled_(false); + this.uiState_ = PrintPreview.UiState_.OPENING_NATIVE_PRINT_DIALOG; + this.nativeLayer_.startShowSystemDialog(); + }, + + /** + * Window onload handler, sets up the page and starts print preview by + * getting the printer list. + * @private + */ + onWindowLoad_: function() { + this.decorate($('print-preview')); + i18nTemplate.process(document, templateData); + if (!this.previewArea_.hasCompatiblePlugin) { + this.setIsEnabled_(false); + } + this.nativeLayer_.startGetInitialSettings(); + this.nativeLayer_.startGetLocalDestinations(); + }, + + /** + * Called when the native layer has initial settings to set. Sets the + * initial settings of the print preview and begins fetching print + * destinations. + * @param {cr.Event} event Contains the initial print preview settings + * persisted through the session. + * @private + */ + onInitialSettingsSet_: function(event) { + assert(this.uiState_ == PrintPreview.UiState_.INITIALIZING, + 'Updating initial settings when not in initializing state: ' + + this.uiState_); + this.uiState_ = PrintPreview.UiState_.READY; + + this.isInKioskAutoPrintMode_ = + event.initialSettings.isInKioskAutoPrintMode; + this.destinationStore_.setInitialDestinationId( + event.initialSettings.initialDestinationId); + this.printTicketStore_.initialize( + event.initialSettings.isDocumentModifiable, + event.initialSettings.isDuplexEnabled, + event.initialSettings.isHeaderFooterEnabled, + event.initialSettings.marginsType, + event.initialSettings.customMargins, + event.initialSettings.thousandsDelimeter, + event.initialSettings.decimalDelimeter, + event.initialSettings.unitType); + }, + + /** + * Calls when the native layer enables Google Cloud Print integration. + * Fetches the user's cloud printers. + * @param {cr.Event} event Contains the base URL of the Google Cloud Print + * service. + * @private + */ + onCloudPrintEnable_: function(event) { + this.cloudPrintInterface_ = new cloudprint.CloudPrintInterface( + event.baseCloudPrintUrl); + this.tracker.add( + this.cloudPrintInterface_, + cloudprint.CloudPrintInterface.EventType.SEARCH_DONE, + this.onCloudPrintSearchDone_.bind(this)); + this.tracker.add( + this.cloudPrintInterface_, + cloudprint.CloudPrintInterface.EventType.PRINTER_DONE, + this.onCloudPrintPrinterDone_.bind(this)); + this.tracker.add( + this.cloudPrintInterface_, + cloudprint.CloudPrintInterface.EventType.SUBMIT_DONE, + this.onCloudPrintSubmitDone_.bind(this)); + this.tracker.add( + this.cloudPrintInterface_, + cloudprint.CloudPrintInterface.EventType.ERROR, + this.onCloudPrintError_.bind(this)); + + var printWithCloudPrintDest = + this.createPrintWithCloudPrintDestination_(); + this.destinationStore_.insertDestination(printWithCloudPrintDest); + + if (cr.isChromeOS) { + this.cloudPrintInterface_.search(true /*isRecent*/); + this.fetchState_ |= PrintPreview.FetchState_.RECENT_CLOUD_DESTINATIONS; + } + }, + + /** + * Called when the native layer gets local destinations. Adds local + * destination objects received from the operating system to the destination + * store. Also adds a save-as-pdf printer. + * @param {cr.Event} Contains the local destinations to set. + * @private + */ + onLocalDestinationsSet_: function(event) { + var localDestinations = []; + for (var destInfo, i = 0; destInfo = event.destinationInfos[i]; i++) { + localDestinations.push( + print_preview.LocalDestinationParser.parse(destInfo)); + } + localDestinations.push(this.createLocalPdfPrintDestination_()); + this.destinationStore_.insertDestinations(localDestinations); + this.fetchState_ &= ~PrintPreview.FetchState_.LOCAL_DESTINATIONS; + }, + + /** + * Called when the native layer retrieves the capabilities for the selected + * local destination. + * @param {cr.Event} event Contains the capabilities of the local print + * destination. + * @private + */ + onLocalDestinationCapabilitiesSet_: function(event) { + // TODO(rltoscano): There may be a race condition here. This method is + // assumed to return capabilities for the currently selected printer. But + // between the time the local printer was selected and the capabilities + // were retrieved, the selected printer can change. One way to address + // this is to include the destination ID in the settingsInfo parameter. + var selectedDestination = this.destinationStore_.selectedDestination; + if (selectedDestination.isLocal) { + var capabilities = print_preview.LocalCapabilitiesParser.parse( + event.settingsInfo); + selectedDestination.capabilities = capabilities; + this.printTicketStore_.updateDestinationCapabilities(capabilities); + this.printIfReady_(); + } + }, + + /** + * Called from native layer after the user was requested to sign in, and did + * so successfully. + * @private + */ + onDestinationsReload_: function() { + this.destinationStore_.clear(); + this.nativeLayer_.startGetLocalDestinations(); + if (this.cloudPrintInterface_) { + // Fetch recent printers. + this.cloudPrintInterface_.search(true /*isRecent*/); + // Fetch the full printer list. + this.cloudPrintInterface_.search(false /*isRecent*/); + } + this.fetchState_ = + PrintPreview.FetchState_.LOCAL_DESTINATIONS | + PrintPreview.FetchState_.ALL_CLOUD_DESTINATIONS | + PrintPreview.FetchState_.RECENT_CLOUD_DESTINATIONS; + }, + + /** + * Called from the native layer when ready to print to Google Cloud Print. + * @param {cr.Event} event Contains the body to send in the HTTP request. + * @private + */ + onPrintToCloud_: function(event) { + assert(this.uiState_ == PrintPreview.UiState_.PRINTING, + 'Document ready to be sent to the cloud when not in printing ' + + 'state: ' + this.uiState_); + assert(this.cloudPrintInterface_ != null, + 'Google Cloud Print is not enabled'); + this.cloudPrintInterface_.submit(event.data); + }, + + /** + * Called from the native layer when the user cancels the save-to-pdf file + * selection dialog. + * @private + */ + onFileSelectionCancel_: function() { + assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION, + 'File selection cancelled when not in file-selection state: ' + + this.uiState_); + this.setIsEnabled_(true); + this.uiState_ = PrintPreview.UiState_.READY; + }, + + /** + * Called from the native layer when save-to-pdf file selection is complete. + * @private + */ + onFileSelectionComplete_: function() { + assert(this.uiState_ == PrintPreview.UiState_.FILE_SELECTION, + 'File selection completed when not in file-selection state: ' + + this.uiState_); + this.previewArea_.showCustomMessage( + localStrings.getString('printingToPDFInProgress')); + this.uiState_ = PrintPreview.UiState_.PRINTING; + }, + + /** + * Called when the Google Cloud Print search API call completes. Adds + * destinations to the printer store and selects one if it matches the + * initial destination. + * @param {cr.Event} event Contains the new cloud destinations. + * @private + */ + onCloudPrintSearchDone_: function(event) { + this.destinationStore_.insertDestinations(event.printers); + if (event.isRecent) { + this.fetchState_ &= ~PrintPreview.FetchState_.RECENT_CLOUD_DESTINATIONS; + } else { + this.fetchState_ &= ~PrintPreview.FetchState_.ALL_CLOUD_DESTINATIONS; + } + }, + + /** + * Called when the Google Cloud Print printer API call completes. Updates + * the UI with the newly received capabilities. + * @param {cr.Event} event Contains the destination returned in the printer + * API call. + */ + onCloudPrintPrinterDone_: function(event) { + var dest = this.destinationStore_.updateDestination(event.printer); + if (this.destinationStore_.selectedDestination == dest) { + this.printTicketStore_.updateDestinationCapabilities(dest.capabilities); + this.printIfReady_(); + } + }, + + /** + * Called after successfully submitting a job to Google Cloud Print. + * @private + */ + onCloudPrintSubmitDone_: function() { + assert(this.uiState_ == PrintPreview.UiState_.PRINTING, + 'Submited job to Google Cloud Print but not in printing state ' + + this.uiState_); + this.close_(); + }, + + /** + * Called when there was an error communicating with Google Cloud print. + * Displays an error message in the print header. + * @param {cr.Event} event Contains the error message. + * @private + */ + onCloudPrintError_: function(event) { + if (cr.isChromeOS && event.message == '403') { + this.nativeLayer_.startCloudPrintSignIn(); + } else { + this.printHeader_.setErrorMessage(event.message); + } + this.fetchState_ &= + ~PrintPreview.FetchState_.RECENT_CLOUD_DESTINATIONS & + ~PrintPreview.FetchState_.ALL_CLOUD_DESTINATIONS; + }, + + /** + * Called when a new destination has been selected. Fetches the + * destination's capability list. + * @private + */ + onDestinationSelect_: function() { + var destination = this.destinationStore_.selectedDestination; + + // Fetch destination capabilities if necessary. + if (!destination.capabilities) { + if (destination.isLocal) { + this.nativeLayer_.startGetLocalDestinationCapabilities( + destination.id); + } else { + assert(this.cloudPrintInterface_ != null, + 'Selected destination is a cloud destination, but Google ' + + 'Cloud Print is not enabled'); + this.cloudPrintInterface_.printer(destination.id); + } + } else { + this.printTicketStore_.updateDestinationCapabilities( + destination.capabilities); + } + + this.printIfReady_(); + }, + + /** + * Called when the preview area's preview generation is in progress. + * @private + */ + onPreviewGenerationInProgress_: function() { + this.isPreviewGenerationInProgress_ = true; + }, + + /** + * Called when the preview area's preview generation is complete. + * @private + */ + onPreviewGenerationDone_: function() { + this.isPreviewGenerationInProgress_ = false; + this.printIfReady_(); + }, + + /** + * Called when the preview area's preview failed to load. + * @private + */ + onPreviewGenerationFail_: function() { + this.isPreviewGenerationInProgress_ = false; + if (this.uiState_ == PrintPreview.UiState_.PRINTING) { + this.nativeLayer_.startCancelPendingPrint(); + } + }, + + /** + * Called when the 'Open pdf in preview' link is clicked. Launches the pdf + * preview app. + * @private + */ + onOpenPdfInPreviewLinkClick_: function() { + assert(this.uiState_ == PrintPreview.UiState_.READY, + 'Trying to open pdf in preview when not in ready state: ' + + this.uiState_); + setIsVisible($('open-preview-app-throbber'), true); + this.previewArea_.showCustomMessage( localStrings.getString('openingPDFInPreview')); - } else if (printToPDF) { - sendPrintDocumentRequest(); - } else if (printWithCloudPrint) { - previewArea.showCustomMessage( - localStrings.getString('printWithCloudPrintWait')); - disableInputElementsInSidebar(); - } else { - isTabHidden = true; - chrome.send('hidePreview'); - } - return; - } - - if (printToPDF || previewAppRequested) { - sendPrintDocumentRequest(); - } else { - window.setTimeout(function() { sendPrintDocumentRequest(); }, 1000); - } -} - -/** - * Sends a message to cancel the pending print request. - */ -function cancelPendingPrintRequest() { - if (isTabHidden) - chrome.send('cancelPendingPrintRequest'); -} - -/** - * Sends a message to initiate print workflow. - */ -function sendPrintDocumentRequest() { - var printerList = $('printer-list'); - var printer = printerList[printerList.selectedIndex]; - chrome.send('saveLastPrinter', [printer.value, cloudprint.getData(printer)]); - - var settings = getSettings(); - if (cr.isMac && previewAppRequested) - settings.OpenPDFInPreview = true; - - chrome.send('print', [JSON.stringify(settings), - cloudprint.getPrintTicketJSON(printer)]); -} - -/** - * Loads the selected preview pages. - */ -function loadSelectedPages() { - pageSettings.updatePageSelection(); - var pageSet = pageSettings.previouslySelectedPages; - var pageCount = pageSet.length; - if (pageCount == 0 || currentPreviewUid == '') - return; - - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); - for (var i = 0; i < pageCount; i++) - onDidPreviewPage(pageSet[i] - 1, currentPreviewUid, lastPreviewRequestID); -} - -/** - * Updates the variables states for preview. - */ -function updateStateForPreview() { - if (!isTabHidden) - previewArea.showLoadingAnimation(); - - if (!hasPendingPreviewRequest && previewModifiable && - hasOnlyPageSettingsChanged()) { - loadSelectedPages(); - generateDraftData = false; - } else { - hasPendingPreviewRequest = true; - generateDraftData = true; - pageSettings.updatePageSelection(); - } - - printSettings.save(); - layoutSettings.updateState(); - previewArea.resetState(); - isPrintReadyMetafileReady = false; - isFirstPageLoaded = false; - - var totalPageCount = pageSettings.totalPageCount; - if (!previewModifiable && totalPageCount > 0) - generateDraftData = false; -} - -/** - * Asks the browser to generate a preview PDF based on current print settings. - */ -function requestPrintPreview() { - updateStateForPreview(); - var totalPageCount = pageSettings.totalPageCount; - var pageCount = totalPageCount || -1; - chrome.send('getPreview', [JSON.stringify(getSettingsWithRequestID()), - pageCount, - previewModifiable]); -} - -/** - * Called from PrintPreviewUI::OnFileSelectionCancelled to notify the print - * preview tab regarding the file selection cancel event. - */ -function fileSelectionCancelled() { - printHeader.enableCancelButton(); -} - -/** - * Called from PrintPreviewUI::OnFileSelectionCompleted to notify the print - * preview tab regarding the file selection completed event. - */ -function fileSelectionCompleted() { - // If the file selection is completed and the tab is not already closed it - // means that a pending print to pdf request exists. - disableInputElementsInSidebar(); - previewArea.showCustomMessage( - localStrings.getString('printingToPDFInProgress')); -} - -/** - * Set the default printer. If there is one, generate a print preview. - * @param {string} printerName Name of the default printer. Empty if none. - * @param {string} cloudPrintData Cloud print related data to restore if - * the default printer is a cloud printer. - */ -function setDefaultPrinter(printerName, cloudPrintData) { - // Add a placeholder value so the printer list looks valid. - addDestinationListOption('', '', true, true, true); - if (printerName) { - defaultOrLastUsedPrinterName = printerName; - if (cloudPrintData) { - cloudprint.setDefaultPrinter(printerName, - cloudPrintData, - addDestinationListOptionAtPosition, - doUpdateCloudPrinterCapabilities); - } else { - $('printer-list')[0].value = defaultOrLastUsedPrinterName; - updateControlsWithSelectedPrinterCapabilities(); - } - } - chrome.send('getPrinters'); -} - -/** - * Fill the printer list drop down. - * Called from PrintPreviewHandler::SetupPrinterList(). - * @param {Array} printers Array of printer info objects. - */ -function setPrinters(printers) { - var printerList = $('printer-list'); - // Remove empty entry added by setDefaultPrinter. - if (printerList[0] && printerList[0].textContent == '') - printerList.remove(0); - for (var i = 0; i < printers.length; ++i) { - var isDefault = (printers[i].deviceName == defaultOrLastUsedPrinterName); - addDestinationListOption(printers[i].printerName, printers[i].deviceName, - isDefault, false, false); - } - - if (printers.length != 0) - addDestinationListOption('', '', false, true, true); - - // Adding option for saving PDF to disk. - addDestinationListOption(localStrings.getString('printToPDF'), - PRINT_TO_PDF, - defaultOrLastUsedPrinterName == PRINT_TO_PDF, - false, - false); - addDestinationListOption('', '', false, true, true); - if (useCloudPrint) { - addDestinationListOption(localStrings.getString('printWithCloudPrint'), - PRINT_WITH_CLOUD_PRINT, - false, - false, - false); - addDestinationListOption('', '', false, true, true); - } - // Add options to manage printers. - if (!cr.isChromeOS) { - addDestinationListOption(localStrings.getString('managePrinters'), - MANAGE_LOCAL_PRINTERS, false, false, false); - } else if (useCloudPrint) { - // Fetch recent printers. - cloudprint.fetchPrinters(addDestinationListOptionAtPosition, false); - // Fetch the full printer list. - cloudprint.fetchPrinters(addDestinationListOptionAtPosition, true); - addDestinationListOption(localStrings.getString('managePrinters'), - MANAGE_CLOUD_PRINTERS, false, false, false); - } - - printerList.disabled = false; - - if (!hasRequestedPreview()) - updateControlsWithSelectedPrinterCapabilities(); -} - -/** - * Creates an option that can be added to the printer destination list. - * @param {string} optionText specifies the option text content. - * @param {string} optionValue specifies the option value. - * @param {boolean} isDefault is true if the option needs to be selected. - * @param {boolean} isDisabled is true if the option needs to be disabled. - * @param {boolean} isSeparator is true if the option is a visual separator and - * needs to be disabled. - * @return {Object} The created option. - */ -function createDestinationListOption(optionText, optionValue, isDefault, - isDisabled, isSeparator) { - var option = document.createElement('option'); - option.textContent = optionText; - option.value = optionValue; - option.selected = isDefault; - option.disabled = isSeparator || isDisabled; - // Adding attribute for improved accessibility. - if (isSeparator) - option.setAttribute('role', 'separator'); - return option; -} - -/** - * Adds an option to the printer destination list. - * @param {string} optionText specifies the option text content. - * @param {string} optionValue specifies the option value. - * @param {boolean} isDefault is true if the option needs to be selected. - * @param {boolean} isDisabled is true if the option needs to be disabled. - * @param {boolean} isSeparator is true if the option serves just as a - * separator. - * @return {Object} The created option. - */ -function addDestinationListOption(optionText, optionValue, isDefault, - isDisabled, isSeparator) { - var option = createDestinationListOption(optionText, - optionValue, - isDefault, - isDisabled, - isSeparator); - $('printer-list').add(option); - return option; -} - -/** - * Adds an option to the printer destination list at a specified position. - * @param {number} position The index in the printer-list wher the option - should be created. - * @param {string} optionText specifies the option text content. - * @param {string} optionValue specifies the option value. - * @param {boolean} isDefault is true if the option needs to be selected. - * @param {boolean} isDisabled is true if the option needs to be disabled. - * @param {boolean} isSeparator is true if the option is a visual separator and - * needs to be disabled. - * @return {Object} The created option. - */ -function addDestinationListOptionAtPosition(position, - optionText, - optionValue, - isDefault, - isDisabled, - isSeparator) { - var option = createDestinationListOption(optionText, - optionValue, - isDefault, - isDisabled, - isSeparator); - var printerList = $('printer-list'); - var before = printerList[position]; - printerList.add(option, before); - return option; -} -/** - * Sets the color mode for the PDF plugin. - * Called from PrintPreviewHandler::ProcessColorSetting(). - * @param {boolean} color is true if the PDF plugin should display in color. - */ -function setColor(color) { - if (!previewArea.pdfPlugin) - return; - - previewArea.pdfPlugin.grayscale(!color); - var printerList = $('printer-list'); - cloudprint.setColor(printerList[printerList.selectedIndex], color); -} - -/** - * Display an error message when print preview fails. - * Called from PrintPreviewMessageHandler::OnPrintPreviewFailed(). - */ -function printPreviewFailed() { - previewArea.displayErrorMessageAndNotify( - localStrings.getString('previewFailed')); -} - -/** - * Display an error message when encountered invalid printer settings. - * Called from PrintPreviewMessageHandler::OnInvalidPrinterSettings(). - */ -function invalidPrinterSettings() { - if (cr.isMac) { - if (previewAppRequested) { - $('open-preview-app-throbber').hidden = true; - previewArea.clearCustomMessageWithDots(); - previewAppRequested = false; - hasPendingPrintDocumentRequest = false; - enableInputElementsInSidebar(); - } - $('open-pdf-in-preview-link').disabled = true; - } - previewArea.displayErrorMessageAndNotify( - localStrings.getString('invalidPrinterSettings')); -} - -/** - * Called when the PDF plugin loads its document. - */ -function onPDFLoad() { - if (previewModifiable) { - setPluginPreviewPageCount(); - } else { - // If the source is pdf, print ready metafile is available only after - // loading the pdf in the plugin. - isPrintReadyMetafileReady = true; - } - // Instruct the plugin which page numbers to display in the page number - // indicator. - previewArea.pdfPlugin.setPageNumbers( - JSON.stringify(pageSettings.selectedPagesSet)); - cr.dispatchSimpleEvent(document, customEvents.PDF_LOADED); - isFirstPageLoaded = true; - checkAndHideOverlayLayerIfValid(); - sendPrintDocumentRequestIfNeeded(); - if (printAutomaticallyInKioskMode) - printHeader.printButton.click(); -} - -function setPluginPreviewPageCount() { - previewArea.pdfPlugin.printPreviewPageCount( - pageSettings.previouslySelectedPages.length); -} - -/** - * Update the page count and check the page range. - * Called from PrintPreviewUI::OnDidGetPreviewPageCount(). - * @param {number} pageCount The number of pages. - * @param {number} previewResponseId The preview request id that resulted in - * this response. - */ -function onDidGetPreviewPageCount(pageCount, previewResponseId) { - if (!isExpectedPreviewResponse(previewResponseId)) - return; - pageSettings.updateState(pageCount); - if (!previewModifiable && pageSettings.requestPrintPreviewIfNeeded()) - return; - - cr.dispatchSimpleEvent(document, customEvents.UPDATE_SUMMARY); -} - -/** - * @param {{contentWidth: number, contentHeight: number, marginLeft: number, - * marginRight: number, marginTop: number, marginBottom: number, - * printableAreaX: number, printableAreaY: number, - * printableAreaWidth: number, printableAreaHeight: number}} pageLayout - * Specifies default page layout details in points. - * @param {boolean} hasCustomPageSizeStyle Indicates whether the previewed - * document has a custom page size style. - */ -function onDidGetDefaultPageLayout(pageLayout, hasCustomPageSizeStyle) { - hasPageSizeStyle = hasCustomPageSizeStyle; - marginSettings.currentDefaultPageLayout = new print_preview.PageLayout( - pageLayout.contentWidth, - pageLayout.contentHeight, - pageLayout.marginLeft, - pageLayout.marginTop, - pageLayout.marginRight, - pageLayout.marginBottom); - headerFooterSettings.checkAndHideHeaderFooterOption( - pageLayout, marginSettings.selectedMarginsValue); -} - -/** - * This function sends a request to hide the overlay layer only if there is no - * pending print document request and we are not waiting for the print ready - * metafile. - */ -function checkAndHideOverlayLayerIfValid() { - var selectedPrinter = getSelectedPrinterName(); - var printToDialog = selectedPrinter == PRINT_TO_PDF || - selectedPrinter == PRINT_WITH_CLOUD_PRINT; - if ((printToDialog || !previewModifiable) && - !isPrintReadyMetafileReady && hasPendingPrintDocumentRequest) { - return; - } - previewArea.hideOverlayLayer(); -} - -/** - * Called when no pipelining previewed pages. - * @param {string} previewUid Preview unique identifier. - * @param {number} previewResponseId The preview request id that resulted in - * this response. - */ -function reloadPreviewPages(previewUid, previewResponseId) { - if (!isExpectedPreviewResponse(previewResponseId)) - return; - - if (!previewModifiable) - previewArea.createOrReloadPDFPlugin(PRINT_READY_DATA_INDEX); - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - checkAndHideOverlayLayerIfValid(); - var pageSet = pageSettings.previouslySelectedPages; - for (var i = 0; i < pageSet.length; i++) { - previewArea.pdfPlugin.loadPreviewPage( - getPageSrcURL(previewUid, pageSet[i] - 1), i); - } - - hasPendingPreviewRequest = false; - isPrintReadyMetafileReady = true; - previewArea.pdfLoaded = true; - sendPrintDocumentRequestIfNeeded(); -} - -/** - * Notification that a print preview page has been rendered. - * Check if the settings have changed and request a regeneration if needed. - * Called from PrintPreviewUI::OnDidPreviewPage(). - * @param {number} pageNumber The page number, 0-based. - * @param {string} previewUid Preview unique identifier. - * @param {number} previewResponseId The preview request id that resulted in - * this response. - */ -function onDidPreviewPage(pageNumber, previewUid, previewResponseId) { - if (!isExpectedPreviewResponse(previewResponseId)) - return; - - // Refactor - if (!previewModifiable) - return; - - if (pageSettings.requestPrintPreviewIfNeeded()) - return; - - var pageIndex = pageSettings.previouslySelectedPages.indexOf(pageNumber + 1); - if (pageIndex == -1) - return; - - currentPreviewUid = previewUid; - if (pageIndex == 0) - previewArea.createOrReloadPDFPlugin(pageNumber); - - previewArea.pdfPlugin.loadPreviewPage( - getPageSrcURL(previewUid, pageNumber), pageIndex); - - if (pageIndex + 1 == pageSettings.previouslySelectedPages.length) { - hasPendingPreviewRequest = false; - if (pageIndex != 0) - sendPrintDocumentRequestIfNeeded(); - } -} - -/** - * Update the print preview when new preview data is available. - * Create the PDF plugin as needed. - * Called from PrintPreviewUI::PreviewDataIsAvailable(). - * @param {string} previewUid Preview unique identifier. - * @param {number} previewResponseId The preview request id that resulted in - * this response. - */ -function updatePrintPreview(previewUid, previewResponseId) { - if (!isExpectedPreviewResponse(previewResponseId)) - return; - - if (!previewModifiable) { - // If the preview is not modifiable the plugin has not been created yet. - currentPreviewUid = previewUid; - hasPendingPreviewRequest = false; - previewArea.createOrReloadPDFPlugin(PRINT_READY_DATA_INDEX); - } else { - isPrintReadyMetafileReady = true; - } - - cr.dispatchSimpleEvent(document, customEvents.UPDATE_PRINT_BUTTON); - if (previewModifiable) - sendPrintDocumentRequestIfNeeded(); -} - -/** - * Checks to see if the requested print data is available for printing and - * sends a print document request if needed. - */ -function sendPrintDocumentRequestIfNeeded() { - if (!hasPendingPrintDocumentRequest || !isFirstPageLoaded) - return; - - // If the selected printer is PRINT_TO_PDF or PRINT_WITH_CLOUD_PRINT or - // the preview source is not modifiable, we need the print ready data for - // printing. If the preview source is modifiable, we need to wait till all - // the requested pages are loaded in the plugin for printing. - var selectedPrinter = getSelectedPrinterName(); - var printToDialog = selectedPrinter == PRINT_TO_PDF || - selectedPrinter == PRINT_WITH_CLOUD_PRINT; - if (((printToDialog || !previewModifiable) && !isPrintReadyMetafileReady) || - (previewModifiable && hasPendingPreviewRequest)) { - return; - } - - hasPendingPrintDocumentRequest = false; - - if (!areSettingsValid()) { - cancelPendingPrintRequest(); - return; - } - sendPrintDocumentRequest(); -} - -/** - * Check if only page selection has been changed since the last preview request - * and is valid. - * @return {boolean} true if the new page selection is valid. - */ -function hasOnlyPageSettingsChanged() { - var tempPrintSettings = new PrintSettings(); - tempPrintSettings.save(); - - return !!(printSettings.deviceName == tempPrintSettings.deviceName && - printSettings.isLandscape == tempPrintSettings.isLandscape && - printSettings.hasHeaderFooter == - tempPrintSettings.hasHeaderFooter && - pageSettings.hasPageSelectionChangedAndIsValid()); -} - -/** - * Called either when there is a scroll event or when the plugin size changes. - */ -function onPreviewPositionChanged() { - marginSettings.onPreviewPositionChanged(); -} - -/** - * @return {boolean} true if a compatible pdf plugin exists. - */ -function checkCompatiblePluginExists() { - var dummyPlugin = $('dummy-viewer'); - var pluginInterface = [dummyPlugin.onload, - dummyPlugin.goToPage, - dummyPlugin.removePrintButton, - dummyPlugin.loadPreviewPage, - dummyPlugin.printPreviewPageCount, - dummyPlugin.resetPrintPreviewUrl, - dummyPlugin.onPluginSizeChanged, - dummyPlugin.onScroll, - dummyPlugin.pageXOffset, - dummyPlugin.pageYOffset, - dummyPlugin.setZoomLevel, - dummyPlugin.setPageNumbers, - dummyPlugin.setPageXOffset, - dummyPlugin.setPageYOffset, - dummyPlugin.getHorizontalScrollbarThickness, - dummyPlugin.getVerticalScrollbarThickness, - dummyPlugin.getPageLocationNormalized, - dummyPlugin.getHeight, - dummyPlugin.getWidth]; - - for (var i = 0; i < pluginInterface.length; i++) { - if (!pluginInterface[i]) - return false; - } - return true; -} - -/** - * Sets the default values and sends a request to regenerate preview data. - * Resets the margin options only if |resetMargins| is true. - * @param {boolean} resetMargins True if margin settings should be resetted. - */ -function setDefaultValuesAndRegeneratePreview(resetMargins) { - if (resetMargins) - marginSettings.resetMarginsIfNeeded(); - pageSettings.resetState(); - requestPrintPreview(); -} - -/** - * Class that represents the state of the print settings. - * @constructor - */ -function PrintSettings() { - this.deviceName = ''; - this.isLandscape = ''; - this.hasHeaderFooter = ''; -} - -/** - * Takes a snapshot of the print settings. - */ -PrintSettings.prototype.save = function() { - this.deviceName = getSelectedPrinterName(); - this.isLandscape = layoutSettings.isLandscape(); - this.hasHeaderFooter = headerFooterSettings.hasHeaderFooter(); -}; - -/** - * Updates the title of the print preview tab according to |initiatorTabTitle|. - * @param {string} initiatorTabTitle The title of the initiator tab. - */ -function setInitiatorTabTitle(initiatorTabTitle) { - if (initiatorTabTitle == '') - return; - document.title = localStrings.getStringF( - 'printPreviewTitleFormat', initiatorTabTitle); -} - -/** - * Closes this print preview tab. - */ -function closePrintPreviewTab() { - window.removeEventListener('keydown', onKeyDown); - chrome.send('closePrintPreviewTab'); - chrome.send('DialogClose'); -} - -/** - * Pass certain directional keyboard events to the PDF viewer. - * @param {Event} e The keydown event. - */ -function tryToHandleDirectionKeyDown(e) { - // Make sure the PDF plugin is there. - if (!previewArea.pdfPlugin) - return; - - // We only care about: PageUp, PageDown, Left, Up, Right, Down. - if (!(e.keyCode == 33 || e.keyCode == 34 || - (e.keyCode >= 37 && e.keyCode <= 40))) { - return; - } - - // If the user is holding a modifier key, ignore. - if (e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) - return; - - // Don't handle the key event for these elements. - var tagName = document.activeElement.tagName; - if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'EMBED') - return; - - // For the most part, if any div of header was the last clicked element, - // then the active element is the body. Starting with the last clicked - // element, and work up the DOM tree to see if any element has a scrollbar. - // If there exists a scrollbar, do not handle the key event here. - var element = document.activeElement; - if (element == document.body) { - if (lastClickedElement) - element = lastClickedElement; - while (element) { - if (element.scrollHeight > element.clientHeight) + this.printDocumentOrOpenPdfPreview_(true /*isPdfPreview*/); + }, + + /** + * Called when the print header's print button is clicked. Prints the + * document. + * @private + */ + onPrintButtonClick_: function() { + assert(this.uiState_ == PrintPreview.UiState_.READY, + 'Trying to print when not in ready state: ' + this.uiState_); + this.printDocumentOrOpenPdfPreview_(false /*isPdfPreview*/); + }, + + /** + * Called when the print header's cancel button is clicked. Closes the + * print dialog. + * @private + */ + onCancelButtonClick_: function() { + this.close_(); + }, + + /** + * Consume escape key presses and ctrl + shift + p. Delegate everything else + * to the preview area. + * @param {KeyboardEvent} e The keyboard event. + * @private + */ + onKeyDown_: function(e) { + // Escape key closes the dialog. + if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey && + !e.metaKey) { + this.close_(); + e.preventDefault(); return; - element = element.parentElement; + } + + // Ctrl + Shift + p / Mac equivalent. + if (e.keyCode == 80) { + if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) || + (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) { + this.openSystemPrintDialog_(); + e.preventDefault(); + return; + } + } + + // Pass certain directional keyboard events to the PDF viewer. + this.previewArea_.handleDirectionalKeyEvent(e); + }, + + /** + * Called when native layer receives invalid settings for a print request. + * @private + */ + onSettingsInvalid_: function() { + this.uiState_ = PrintPreview.UiState_.ERROR; + this.previewArea_.showCustomMessage( + localStrings.getString('invalidPrinterSettings')); + }, + + /** + * Called when the native layer dispatches a DISABLE_SCALING event. Updates + * the print ticket. + * @private + */ + onDisableScaling_: function() { + this.printTicketStore_.updateFitToPage(false); + }, + + /** + * Called when the user selects the "Manage printers..." option in the + * destination select. + * @private + */ + onManagePrinters_: function() { + if (cr.isChromeOS) { + this.nativeLayer_.startManageCloudPrinters(); + } else { + this.nativeLayer_.startManageLocalPrinters(); + } } - } - - // No scroll bar anywhere, or the active element is something else, like a - // button. Note: buttons have a bigger scrollHeight than clientHeight. - previewArea.pdfPlugin.sendKeyEvent(e.keyCode); - e.preventDefault(); -} - -/** - * Handle keyboard events. - * @param {KeyboardEvent} e The keyboard event. - */ -function onKeyDown(e) { - // Escape key closes the dialog. - if (e.keyCode == 27 && !e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) { - printHeader.disableCancelButton(); - closePrintPreviewTab(); - e.preventDefault(); - } - // Ctrl + Shift + p / Mac equivalent. - if (e.keyCode == 80) { - if ((cr.isMac && e.metaKey && e.altKey && !e.shiftKey && !e.ctrlKey) || - (!cr.isMac && e.shiftKey && e.ctrlKey && !e.altKey && !e.metaKey)) { - window.removeEventListener('keydown', onKeyDown); - onSystemDialogLinkClicked(); - e.preventDefault(); - } - } - - tryToHandleDirectionKeyDown(e); -} - -window.addEventListener('DOMContentLoaded', onLoad); -window.addEventListener('keydown', onKeyDown); - -/// Pull in all other scripts in a single shot. + }; + + // Export + return { + PrintPreview: PrintPreview + }; +}); + +// Pull in all other scripts in a single shot. +<include src="data/page_number_set.js"/> +<include src="data/destination.js"/> +<include src="data/local_parsers.js"/> +<include src="data/cloud_parsers.js"/> +<include src="data/chromium_capabilities.js"/> +<include src="data/cloud_capabilities.js"/> +<include src="data/destination_store.js"/> +<include src="data/margins.js"/> +<include src="data/document_info.js"/> +<include src="data/printable_area.js"/> +<include src="data/measurement_system.js"/> +<include src="data/print_ticket_store.js"/> +<include src="data/coordinate2d.js"/> +<include src="data/size.js"/> +<include src="data/capabilities_holder.js"/> + +<include src="data/ticket_items/ticket_item.js"/> + +<include src="data/ticket_items/custom_margins.js"/> +<include src="data/ticket_items/collate.js"/> +<include src="data/ticket_items/color.js"/> +<include src="data/ticket_items/copies.js"/> +<include src="data/ticket_items/duplex.js"/> +<include src="data/ticket_items/header_footer.js"/> +<include src="data/ticket_items/landscape.js"/> +<include src="data/ticket_items/margins_type.js"/> +<include src="data/ticket_items/page_range.js"/> +<include src="data/ticket_items/fit_to_page.js"/> + +<include src="native_layer.js"/> <include src="print_preview_animations.js"/> -<include src="print_preview_cloud.js"/> +<include src="cloud_print_interface.js"/> <include src="print_preview_utils.js"/> <include src="print_header.js"/> -<include src="page_settings.js"/> -<include src="copies_settings.js"/> -<include src="header_footer_settings.js"/> -<include src="fit_to_page_settings.js"/> -<include src="layout_settings.js"/> -<include src="color_settings.js"/> -<include src="margin_settings.js"/> -<include src="margin_textbox.js"/> -<include src="margin_utils.js"/> -<include src="margins_ui.js"/> -<include src="margins_ui_pair.js"/> -<include src="more_options.js"/> -<include src="preview_area.js"/> + +<include src="settings/page_settings.js"/> +<include src="settings/copies_settings.js"/> +<include src="settings/layout_settings.js"/> +<include src="settings/color_settings.js"/> +<include src="settings/margin_settings.js"/> +<include src="settings/destination_settings.js"/> +<include src="settings/other_options_settings.js"/> + +<include src="previewarea/margin_control.js"/> +<include src="previewarea/margin_control_container.js"/> +<include src="previewarea/preview_area.js"/> +<include src="preview_generator.js"/> + +var printPreview = new print_preview.PrintPreview(); diff --git a/chrome/browser/resources/print_preview/print_preview_cloud.js b/chrome/browser/resources/print_preview/print_preview_cloud.js deleted file mode 100644 index 7c8b7c0..0000000 --- a/chrome/browser/resources/print_preview/print_preview_cloud.js +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -cr.define('cloudprint', function() { - - // The URL to use to access the cloud print servers. - // Set by a call to setBaseURL. - var cloudPrintBaseURL = ''; - - // Headers to set for most cloud print API calls. - var xCloudPrintURLHeader = {'Content-Type': - 'application/x-www-form-urlencoded', - 'X-CloudPrint-Proxy': 'ChromePrintPreview'}; - - // Headers to set when sending multipart data to cloud print APIs. - // Currently only used when submitting a job. - var xCloudPrintFormHeader = {'Content-Type': - 'multipart/form-data; boundary=----CloudPrintFormBoundaryjc9wuprokl8i', - 'X-CloudPrint-Proxy': 'ChromePrintPreview'}; - - // The last received XSRF token. This should be sent with each request - // to prevent XSRF. - var lastXSRFToken = ''; - - /** - * Sets the base URL to be used for communicating with cloud print - * servers. - * @param {string} cloudPrintURL The URL to use. - */ - function setBaseURL(cloudPrintURL) { - cloudPrintBaseURL = cloudPrintURL; - } - - /** - * Gets the base URL to be used for communicating with cloud print - * servers. - * @return {string} The URL. - */ - function getBaseURL() { - return cloudPrintBaseURL; - } - - /** - * Extracts the XSRF token from each response to be used in the next - * request. - * @param {XMLHttpRequest} xhr The object used to make the request. - * @return {string} The extracted XSRF token. - */ - function extractXSRFtoken(xhr) { - if (xhr.status == 200) { - var result = JSON.parse(xhr.responseText); - return result['xsrf_token']; - } else { - return null; - } - } - - /** - * Makes a request to cloud print servers. - * @param {string} method The HTTP method to be used. - * @param {string} action The cloud print API to call. - * @param {Array} headers Headers to send with the request. - * @param {string} body Body to be sent with POST requests. - * @param {function} callback Function to be called to process response. - * @param {boolean} async True if we want the request made asyncronously. - */ - function sendCloudPrintRequest(method, - action, - headers, - params, - body, - callback) { - var xhr = new XMLHttpRequest(); - if (callback != null) { - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - var updatedXSRFToken = extractXSRFtoken(xhr); - if (updatedXSRFToken != null) { - lastXSRFToken = updatedXSRFToken; - } - callback.call(this, xhr); - } - }; - } - var url = cloudPrintBaseURL + '/' + action; - if (params == null) { - params = new Array; - } - if (lastXSRFToken.length != 0) { - params.push('xsrf=' + lastXSRFToken); - } - if (params.length != 0) { - url = url + '?'; - for (param in params) { - url = url + params[param] + '&'; - } - } - xhr.open(method, url, true); - xhr.withCredentials = true; - if (headers) { - for (var header in headers) { - if (headers.hasOwnProperty(header)) { - xhr.setRequestHeader(header, headers[header]); - } - } - } - xhr.send(body); - return xhr; - } - - /** - * Parse the response from the fetchPrinters call. - * @param {function} callback Function to be called to process response. - * @param {XMLHttpRequest} xhr The object used to make the request. - */ - function fetchPrintersResponse(callback, xhr) { - if (xhr.status == 200) { - var searchResult = JSON.parse(xhr.responseText); - if (searchResult['success']) { - var printerList = searchResult['printers']; - addCloudPrinters(printerList, callback); - return; - } - } - addCloudPrinters(null, callback); - } - - /** - * Retrieve the list of printers available via cloud print. - * @param {function} callback Function to be called to process response. - */ - function fetchPrinters(callback, all) { - var query = 'q=^recent'; - if (all) { - query = ''; - } - sendCloudPrintRequest('GET', - 'search', - xCloudPrintURLHeader, - [query], - null, - fetchPrintersResponse.bind(this, callback)); - } - - /** - * Handle the response from printing to cloud print. - * @param {function} callback Function to be called to process response. - * @param {XMLHttpRequest} xhr The object used to make the request. - */ - function printToCloudResponse(callback, xhr) { - if (xhr.status == 200) { - var printResult = JSON.parse(xhr.responseText); - if (printResult['success']) { - callback.call(); - } - } - // TODO(abodenha@chromium.org) Catch and handle failures. - } - - /** - * Send the current document to cloud print. - * @param {string} data The document to be printed. - * @param {function} callback Function to be called to process response. - */ - function printToCloud(data, callback) { - // TODO(abodenha@chromium.org) Make sure we have an XSRF token before - // sending a submit. Right now if the user clicks print before we - // complete any request we wont have an XSRF and the submit will fail. - sendCloudPrintRequest('POST', - 'submit', - xCloudPrintFormHeader, - null, - data, - printToCloudResponse.bind(this, callback)); - } - - /** - * Gets the JSON string used to control the behavior of the current - * print job. - * @param {Object} printer The printer object to get the ticket for. - * @return {string} The print ticket or null if not a cloud printer. - */ - function getPrintTicketJSON(printer) { - if (isCloudPrint(printer)) { - return JSON.stringify({'capabilities': - [printer.cloudPrintOptions.colorOption]}); - } else { - return null; - } - } - - /** - * Process the response from cloud print containing the capabilities - * for the printer. - * @param {function} callback Function to be called to process response. - * @param {Object} printer The printer object to get the capabilites for. - * @param {XMLHttpRequest} xhr The object used to make the request. - */ - function updatePrinterCapsResponse(callback, printer, xhr) { - if (xhr.status == 200) { - var printResult = JSON.parse(xhr.responseText); - if (printResult['success']) { - if (!printer.cloudPrintOptions) - printer.cloudPrintOptions = new Object; - printer.cloudPrintOptions.capsDownloaded = true; - printer.cloudPrintOptions.colorOption = null; - printer.cloudPrintOptions.colorIsDefault = false; - var detailedCapabilities = printResult.printers[0].capabilities; - for (var capability in detailedCapabilities) { - var cap = detailedCapabilities[capability]; - if (cap.name == 'ns1:Colors') { - printer.cloudPrintOptions.colorOption = new Object; - printer.cloudPrintOptions.colorOption.name = cap.name; - printer.cloudPrintOptions.colorOption.type = cap.type; - for (var option in cap.options) { - var opt = cap.options[option]; - if (opt.name == 'Color') { - printer.cloudPrintOptions.colorOnOption = opt; - } - if (opt.name == 'Grey_K') { - printer.cloudPrintOptions.colorOffOption = opt; - } - if (opt.default) { - printer.cloudPrintOptions.colorOption.options = [opt]; - printer.cloudPrintOptions.colorIsDefault = - opt.name == printer.cloudPrintOptions.colorOnOption.name; - } - } - } - } - callback.call(this, printer); - } - } - } - - /** - * Retrieve capabilities for a printer. - * @param {Object} printer The printer object to get the capabilities for. - * @param {function} callback Function to be called to process response. - */ - function updatePrinterCaps(printer, callback) { - if (isCloudPrint(printer) && !printer.cloudPrintOptions.capsDownloaded) { - sendCloudPrintRequest('GET', - 'printer?printerid=' + - printer.value + - '&output=json', - xCloudPrintURLHeader, - null, - null, - updatePrinterCapsResponse.bind(this, - callback, - printer)); - } else { - callback.call(this, printer); - } - } - - /** - * @param {Object} printer The printer object to get the data for. - * @return {boolean} true if the printer supports color. - */ - function supportsColor(printer) { - return isCloudPrint(printer) && - printer.cloudPrintOptions.colorOption != null; - } - - /** - * @param {Object} printer The printer object to get the data for. - * @return {boolean} true if the printer defaults to color. - */ - function colorIsDefault(printer) { - // For now assume that unsupported color means we just don't know - // and assume color. - return !supportsColor(printer) || - (isCloudPrint(printer) && printer.cloudPrintOptions.colorIsDefault); - } - - /** - * Turn color on or off for the specified printer. - * @param {Object} printer The printer object to turn color on/off for. - * @param {boolean} color True to turn color on. - */ - function setColor(printer, color) { - if (isCloudPrint(printer) && supportsColor(printer)) { - if (color) { - printer.cloudPrintOptions.colorOption.options = - [printer.cloudPrintOptions.colorOnOption]; - } else { - printer.cloudPrintOptions.colorOption.options = - [printer.cloudPrintOptions.colorOffOption]; - } - } - } - - /** - * Creates a cloud print printer and sets it as the default printer. - * @param {string} printer_name The name of the printer to create. - * @param {Object} cloud_print_data Data to be stored in cloudPrintOptions. - * @param {function} add_callback The callback to be called to add the new - * printer to the print preview UI. - * @param {function} update_caps_callback The callback to be called to update - * capabilities on the new printer. - */ - function setDefaultPrinter(printer_name, - cloud_print_data, - add_callback, - update_caps_callback) { - var printer = addCloudPrinters([JSON.parse(cloud_print_data)], - add_callback); - if (printer) - update_caps_callback(printer); - } - - /** - * Returns the data necessary to serialize a cloud print printer. - * @param {Object} printer The printer object to get data for. - * @return {string} A JSON string that can be used to recreate the - * cloud print portion of the printer object, or and empty string if - * there is no data to save. - */ - function getData(printer) { - if (isCloudPrint(printer)) { - return JSON.stringify(printer.cloudPrintOptions); - } else { - return ''; - } - } - - /** - * Test if a printer is a cloud print printer. - * @param {Object} printer The printer to test. - * @return {boolean} true iff the printer is a cloud print printer. - */ - function isCloudPrint(printer) { - return printer && printer.cloudPrintOptions != null; - } - - /** - * Mark a printer as a cloud print printer and record its name and id. - * @param {Object} printer The printer to mark. - * @param {string} name The user visible name of the printer. - * @param {string} id The id of the printer used by cloud print to - * identify it. - */ - function setCloudPrint(printer, name, id) { - if (!printer.cloudPrintOptions) { - printer.cloudPrintOptions = new Object; - } - printer.cloudPrintOptions.name = name; - printer.cloudPrintOptions.id = id; - } - - /** - * Test if a particular cloud printer has already been added to the - * printer dropdown. - * @param {string} id A unique value to track this printer. - * @return {boolean} True if |id| has previously been passed to - * trackCloudPrinterAdded. - */ - function cloudPrinterAlreadyAdded(id) { - return addedCloudPrinters[id]; - } - - /** - * Test if a particular printer has already been added to the printers - * dropdown. Records it if not. - * @param {string} id A unique value to track this printer. - * @return {boolean} False if adding this printer would exceed - * |maxCloudPrinters|. - */ - function trackCloudPrinterAdded(id) { - if (Object.keys(addedCloudPrinters).length < maxCloudPrinters) { - addedCloudPrinters[id] = true; - return true; - } else { - return false; - } - } - - /** - * Add cloud printers to the list drop down. - * Called from the cloudprint object on receipt of printer information from - * the cloud print server. - * @param {Array} printers Array of printer info objects. - * @return {Object} The currently selected printer. - */ - function addCloudPrinters(printers, addDestinationListOptionAtPosition) { - var isFirstPass = false; - var printerList = $('printer-list'); - - if (firstCloudPrintOptionPos == lastCloudPrintOptionPos) { - isFirstPass = true; - // Remove empty entry added by setDefaultPrinter. - if (printerList[0] && printerList[0].textContent == '') - printerList.remove(0); - } - if (printers != null) { - for (var i = 0; i < printers.length; i++) { - if (!cloudPrinterAlreadyAdded(printers[i]['id'])) { - if (!trackCloudPrinterAdded(printers[i]['id'])) { - break; - } - if (printers[i]['displayName'] && printers[i]['displayName'] != '') - var name = printers[i]['displayName']; - else - var name = printers[i]['name']; - - var option = addDestinationListOptionAtPosition( - lastCloudPrintOptionPos++, - name, - printers[i]['id'], - name == defaultOrLastUsedPrinterName, - false, - false); - cloudprint.setCloudPrint(option, - name, - printers[i]['id']); - } - } - } else { - if (!cloudPrinterAlreadyAdded(SIGN_IN)) { - addDestinationListOptionAtPosition(lastCloudPrintOptionPos++, - localStrings.getString('signIn'), - SIGN_IN, - false, - false, - false); - trackCloudPrinterAdded(SIGN_IN); - chrome.send('signIn'); - } - } - var selectedPrinter = printerList.selectedIndex; - if (selectedPrinter < 0) - return null; - return printerList.options[selectedPrinter]; - } - - return { - addCloudPrinters: addCloudPrinters, - colorIsDefault: colorIsDefault, - fetchPrinters: fetchPrinters, - getBaseURL: getBaseURL, - getData: getData, - getPrintTicketJSON: getPrintTicketJSON, - isCloudPrint: isCloudPrint, - printToCloud: printToCloud, - setBaseURL: setBaseURL, - setCloudPrint: setCloudPrint, - setColor: setColor, - setDefaultPrinter: setDefaultPrinter, - supportsColor: supportsColor, - updatePrinterCaps: updatePrinterCaps - }; -}); diff --git a/chrome/browser/resources/print_preview/print_preview_utils.js b/chrome/browser/resources/print_preview/print_preview_utils.js index c761650..35bb80a 100644 --- a/chrome/browser/resources/print_preview/print_preview_utils.js +++ b/chrome/browser/resources/print_preview/print_preview_utils.js @@ -187,81 +187,19 @@ function pageSetToPageRanges(pageSet) { } /** - * Constructs a url for getting a specific page. - * @param {string} id The id of the preview data. - * @param {number} pageNumber The number of the desired page. - * @return {string} The constructed URL. + * Shows or hides an element. + * @param {Element} element Element to show or hide. + * @param {boolean} isVisible Whether the element should be visible or not. */ -function getPageSrcURL(id, pageNumber) { - return 'chrome://print/' + id + '/' + pageNumber + '/print.pdf'; +function setIsVisible(element, isVisible) { + element.style.display = isVisible ? '' : 'none'; } /** - * Returns a random integer within the specified range, |endPointA| and - * |endPointB| are included. - * @param {number} endPointA One end of the desired range. - * @param {number} endPointB The other end of the desired range. - * @return {number} The random integer. + * @param {Array.<object>} array Array to check for item. + * @param {object} item Item to look for in array. + * @return {boolean} Whether the item is in the array. */ -function randomInteger(endPointA, endPointB) { - var from = Math.min(endPointA, endPointB); - var to = Math.max(endPointA, endPointB); - return Math.floor(Math.random() * (to - from + 1) + from); -} - -// Number of points per inch. -var POINTS_PER_INCH = 72; -// Number of points per millimeter. -var POINTS_PER_MILLIMETER = 2.83464567; - -/** - * Converts |value| from inches to points. - * @param {number} value The number in inches. - * @return {number} |value| in points. - */ -function convertInchesToPoints(value) { - return value * POINTS_PER_INCH; -} - -/** - * Converts |value| from points to inches. - * @param {number} value The number in points. - * @return {number} |value| in inches. - */ -function convertPointsToInches(value) { - return value / POINTS_PER_INCH; -} - -/** - * Converts |value| from millimeters to points. - * @param {number} value The number in millimeters. - * @return {number} |value| in points. - */ -function convertMillimetersToPoints(value) { - return value * POINTS_PER_MILLIMETER; -} - -/** - * Converts |value| from points to millimeters. - * @param {number} value The number in points. - * @return {number} |value| in millimeters. - */ -function convertPointsToMillimeters(value) { - return value / POINTS_PER_MILLIMETER; -} - -/** - * Parses |numberFormat| and extracts the symbols used for the thousands point - * and decimal point. - * @param {string} numberFormat The formatted version of the number 12345678. - * @return {!Array.<string>} The extracted symbols in the order - * [thousandsSymbol, decimalSymbol]]. For example - * parseNumberFormat("123,456.78") returns [",", "."]. - */ -function parseNumberFormat(numberFormat) { - if (!numberFormat) - numberFormat = ''; - var regex = /^(\d+)(\W{0,1})(\d+)(\W{0,1})(\d+)$/; - var matches = numberFormat.match(regex) || ['', '', ',', '', '.']; - return [matches[2], matches[4]]; +function arrayContains(array, item) { + return array.indexOf(item) != -1; } diff --git a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs index adaee85..5f674b7 100644 --- a/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs +++ b/chrome/browser/resources/print_preview/print_preview_utils_unittest.gtestjs @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,7 +7,9 @@ * @constructor * @extends {testing.Test} */ -function PrintPreviewUtilsUnitTest () {} +function PrintPreviewUtilsUnitTest () { + testing.Test.call(this); +} PrintPreviewUtilsUnitTest.prototype = { __proto__: testing.Test.prototype, @@ -15,7 +17,7 @@ PrintPreviewUtilsUnitTest.prototype = { /** @inheritDoc */ extraLibraries: [ 'print_preview_utils.js', - ], + ] }; TEST_F('PrintPreviewUtilsUnitTest', 'IsInteger', function() { @@ -113,21 +115,3 @@ TEST_F('PrintPreviewUtilsUnitTest', 'PageSetToPageRanges', function() { assertEquals(pageRanges[2].from, 11); assertEquals(pageRanges[2].to, 11); }); - -TEST_F('PrintPreviewUtilsUnitTest', 'ParseNumberFormat', function() { - assertTrue(areArraysEqual(['.', ','], parseNumberFormat('123.456,78'))); - assertTrue(areArraysEqual(['.', '.'], parseNumberFormat('123.456.78'))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat('123,456.78'))); - assertTrue(areArraysEqual([',', ','], parseNumberFormat('123,456,78'))); - assertTrue(areArraysEqual([' ', ','], parseNumberFormat('123 456,78'))); - assertTrue(areArraysEqual([' ', '.'], parseNumberFormat('123 456.78'))); - assertTrue(areArraysEqual([' ', ' '], parseNumberFormat('123 456 78'))); - assertTrue(areArraysEqual(['', ''], parseNumberFormat('123'))); - - assertTrue(areArraysEqual([',', '.'], parseNumberFormat('abcdef'))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat(null))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat(undefined))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat(''))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat('1'))); - assertTrue(areArraysEqual([',', '.'], parseNumberFormat('12'))); -}); diff --git a/chrome/browser/resources/print_preview/settings/color_settings.html b/chrome/browser/resources/print_preview/settings/color_settings.html new file mode 100644 index 0000000..7b742e9 --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/color_settings.html @@ -0,0 +1,17 @@ +<div id="color-settings" + class="color-settings two-column visible" + aria-hidden="false" + aria-live="polite"> + <h1 i18n-content="optionColor"></h1> + <div class="right-column"> + <div class="radio"><label> + <input class="color-settings-color-option" type="radio" name="color"/> + <span i18n-content="optionColor"></span> + </label></div> + <div class="radio"><label> + <input class="color-settings-bw-option" type="radio" name="color" + checked/> + <span i18n-content="optionBw"></span> + </label></div> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/color_settings.js b/chrome/browser/resources/print_preview/settings/color_settings.js new file mode 100644 index 0000000..eb35e65 --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/color_settings.js @@ -0,0 +1,133 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a ColorSettings object. This object encapsulates all settings and + * logic related to color selection (color/bw). + * @param {!print_preview.PrintTicketStore} printTicketStore Used for writing + * to the print ticket. + * @constructor + * @extends {print_preview.Component} + */ + function ColorSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used for writing to the print ticket. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + }; + + /** + * CSS classes used by the color settings. + * @enum {string} + * @private + */ + ColorSettings.Classes_ = { + BW_OPTION: 'color-settings-bw-option', + COLOR_OPTION: 'color-settings-color-option' + }; + + ColorSettings.prototype = { + __proto__: print_preview.Component.prototype, + + set isEnabled(isEnabled) { + this.colorRadioButton_.disabled = !isEnabled; + this.bwRadioButton_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.addEventListeners_(); + }, + + get colorRadioButton_() { + return this.getElement().getElementsByClassName( + ColorSettings.Classes_.COLOR_OPTION)[0]; + }, + + get bwRadioButton_() { + return this.getElement().getElementsByClassName( + ColorSettings.Classes_.BW_OPTION)[0]; + }, + + /** + * Adding listeners to all targets and UI controls. + * @private + */ + addEventListeners_: function() { + this.tracker.add( + this.colorRadioButton_, + 'click', + this.updatePrintTicket_.bind(this, true)); + this.tracker.add( + this.bwRadioButton_, + 'click', + this.updatePrintTicket_.bind(this, false)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onCapabilitiesChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onCapabilitiesChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onCapabilitiesChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onTicketChange_.bind(this)); + }, + + /** + * Updates print ticket with whether the document should be printed in + * color. + * @param {boolean} isColor Whether the document should be printed in color. + * @private + */ + updatePrintTicket_: function(isColor) { + this.printTicketStore_.updateColor(isColor); + }, + + /** + * Called when the ticket store's capabilities have changed. Shows or hides + * the color settings. + * @private + */ + onCapabilitiesChange_: function() { + if (this.printTicketStore_.hasColorCapability()) { + fadeInOption(this.getElement()); + var isColorEnabled = this.printTicketStore_.isColorEnabled(); + this.colorRadioButton_.checked = isColorEnabled; + this.bwRadioButton_.checked = !isColorEnabled; + } else { + fadeOutOption(this.getElement()); + } + this.getElement().setAttribute( + 'aria-hidden', !this.printTicketStore_.hasColorCapability()); + }, + + onTicketChange_: function() { + if (this.printTicketStore_.hasColorCapability()) { + var isColorEnabled = this.printTicketStore_.isColorEnabled(); + this.colorRadioButton_.checked = isColorEnabled; + this.bwRadioButton_.checked = !isColorEnabled; + } + } + }; + + // Export + return { + ColorSettings: ColorSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/settings/copies_settings.css b/chrome/browser/resources/print_preview/settings/copies_settings.css new file mode 100644 index 0000000..129943f --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/copies_settings.css @@ -0,0 +1,56 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#copies-settings .copies-settings-copies { + position: relative; + width: 2.75em; +} + +#copies-settings .copies-settings-copies.invalid { + background: rgb(255, 240, 240); + color: rgb(140, 20, 20); +} + +#copies-settings .copies-settings-increment:focus, +#copies-settings .copies-settings-decrement:focus, +.copies-settings:focus { + z-index: 1; +} + +#copies-settings .copies-settings-collate { + -webkit-padding-start: 16px; + display: inline-block; +} + +#copies-settings .copies-settings-increment, +#copies-settings .copies-settings-decrement { + -webkit-padding-end: 0; + -webkit-padding-start: 0; + font-weight: 600; + margin: 0; + min-width: 0; + position: relative; + width: 2em; +} + +#copies-settings .copies-settings-increment { + -webkit-margin-start: -5px; + border-radius: 0; +} + +#copies-settings .copies-settings-decrement { + -webkit-margin-start: -5px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 3px; + border-top-left-radius: 0; + border-top-right-radius: 3px; +} + +html[dir='rtl'] #copies-settings .copies-settings-decrement { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 0; + border-top-left-radius: 3px; + border-top-right-radius: 0; +} diff --git a/chrome/browser/resources/print_preview/settings/copies_settings.html b/chrome/browser/resources/print_preview/settings/copies_settings.html new file mode 100644 index 0000000..b8218df --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/copies_settings.html @@ -0,0 +1,31 @@ +<div id="copies-settings" class="copies-settings two-column visible"> + <h1 i18n-content="copiesLabel"></h1> + <div class="right-column"> + <div> + <input class="copies-settings-copies" type="text" value="1" maxlength="3"> + <button class="copies-settings-increment" + i18n-values="title:incrementTitle;">+</button> + <button class="copies-settings-decrement" + i18n-values="title:decrementTitle;">–</button> + <div class="copies-settings-collate checkbox" + aria-live="polite" hidden> + <label> + <input class="copies-settings-collate-checkbox" + type="checkbox" + checked/> + <span i18n-content="optionCollate"></span> + </label> + </div> + </div> + <span class="copies-settings-hint hint" + i18n-content="copiesInstruction" + aria-live="polite"> + </span> + <div class="checkbox"> + <label class="copies-settings-duplex" aria-live="polite"> + <input class="copies-settings-duplex-checkbox" type="checkbox"/> + <span i18n-content="optionTwoSided"></span> + </label> + </div> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/copies_settings.js b/chrome/browser/resources/print_preview/settings/copies_settings.js new file mode 100644 index 0000000..f2baecd --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/copies_settings.js @@ -0,0 +1,327 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Component that renders the copies settings UI. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and + * write the copies settings. + * @constructor + * @extends {print_preview.Component} + */ + function CopiesSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used for writing to the print ticket and validating inputted values. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Timeout used to delay processing of the copies input. + * @type {Object} + * @private + */ + this.textfieldTimeout_ = null; + + /** + * Whether this component is enabled or not. + * @type {boolean} + * @private + */ + this.isEnabled_ = true; + + /** + * Textfield for entering copies values. + * @type {HTMLInputElement} + * @private + */ + this.textfield_ = null; + + /** + * Increment button used to increment the copies value. + * @type {HTMLButtonElement} + * @private + */ + this.incrementButton_ = null; + + /** + * Decrement button used to decrement the copies value. + * @type {HTMLButtonElement} + * @private + */ + this.decrementButton_ = null; + + /** + * Container div for the collate checkbox. + * @type {HTMLDivElement} + * @private + */ + this.collateDiv_ = null; + + /** + * Checkbox used to enable/disable collation. + * @type {HTMLInputElement} + * @private + */ + this.collateCheckbox_ = null; + + /** + * Container div for the duplex checkbox. + * @type {HTMLDivElement} + * @private + */ + this.duplexDiv_ = null; + + /** + * Checkbox used to enable/disable duplexing. + * @type {HTMLInputElement} + * @private + */ + this.duplexCheckbox_ = null; + + /** + * Hint element used to show a hint when the copies value is invalid. + * @type {HTMLElement} + * @private + */ + this.hintEl_ = null; + }; + + /** + * CSS classes used by the component. + * @enum {string} + * @private + */ + CopiesSettings.Classes_ = { + COPIES: 'copies-settings-copies', + INCREMENT: 'copies-settings-increment', + DECREMENT: 'copies-settings-decrement', + HINT: 'copies-settings-hint', + COLLATE: 'copies-settings-collate', + COLLATE_CHECKBOX: 'copies-settings-collate-checkbox', + DUPLEX: 'copies-settings-duplex', + DUPLEX_CHECKBOX: 'copies-settings-duplex-checkbox' + }; + + /** + * Delay in milliseconds before processing the textfield. + * @type {number} + * @private + */ + CopiesSettings.TEXTFIELD_DELAY_ = 250; + + CopiesSettings.prototype = { + __proto__: print_preview.Component.prototype, + + /** @param {boolean} isEnabled Whether the copies settings is enabled. */ + set isEnabled(isEnabled) { + this.textfield_.disabled = !isEnabled; + this.collateCheckbox_.disabled = !isEnabled; + this.duplexCheckbox_.disabled = !isEnabled; + this.isEnabled_ = isEnabled; + if (isEnabled) { + this.updateState_(); + } else { + this.textfield_.disabled = true; + this.incrementButton_.disabled = true; + this.decrementButton_.disabled = true; + this.duplexCheckbox_.disabled = true; + } + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + + this.tracker.add( + this.textfield_, 'keyup', this.onTextfieldKeyUp_.bind(this)); + this.tracker.add( + this.textfield_, 'blur', this.onTextfieldBlur_.bind(this)); + this.tracker.add( + this.incrementButton_, 'click', this.onButtonClicked_.bind(this, 1)); + this.tracker.add( + this.decrementButton_, 'click', this.onButtonClicked_.bind(this, -1)); + this.tracker.add( + this.duplexCheckbox_, + 'click', + this.onDuplexCheckboxClick_.bind(this)); + this.tracker.add( + this.collateCheckbox_, + 'click', + this.onCollateCheckboxClick_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.updateState_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.updateState_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.updateState_.bind(this)); + }, + + /** @override */ + exitDocument: function() { + print_preview.Component.prototype.exitDocument.call(this); + this.textfield_ = null; + this.incrementButton_ = null; + this.decrementButton_ = null; + this.collateDiv_ = null; + this.collateCheckbox_ = null; + this.duplexDiv_ = null; + this.duplexCheckbox_ = null; + this.hintEl_ = null; + }, + + /** @override */ + decorateInternal: function() { + this.textfield_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.COPIES)[0]; + this.incrementButton_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.INCREMENT)[0]; + this.decrementButton_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.DECREMENT)[0]; + this.collateDiv_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.COLLATE)[0]; + this.collateCheckbox_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.COLLATE_CHECKBOX)[0]; + this.duplexDiv_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.DUPLEX)[0]; + this.duplexCheckbox_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.DUPLEX_CHECKBOX)[0]; + this.hintEl_ = this.getElement().getElementsByClassName( + CopiesSettings.Classes_.HINT)[0]; + }, + + /** + * Updates the state of the copies settings UI controls. + * @private + */ + updateState_: function() { + if (!this.printTicketStore_.hasCopiesCapability()) { + fadeOutOption(this.getElement()); + return; + } + + if (this.textfield_.value != this.printTicketStore_.getCopiesStr()) { + this.textfield_.value = this.printTicketStore_.getCopiesStr(); + } + + var currentValueGreaterThan1 = false; + if (this.printTicketStore_.isCopiesValid()) { + this.textfield_.classList.remove('invalid'); + fadeOutElement(this.hintEl_); + this.hintEl_.setAttribute('aria-hidden', true); + var currentValue = parseInt(this.printTicketStore_.getCopiesStr()); + var currentValueGreaterThan1 = currentValue > 1; + this.incrementButton_.disabled = + !this.isEnabled_ || + !this.printTicketStore_.isCopiesValidForValue(currentValue + 1); + this.decrementButton_.disabled = + !this.isEnabled_ || + !this.printTicketStore_.isCopiesValidForValue(currentValue - 1); + } else { + this.textfield_.classList.add('invalid'); + this.hintEl_.setAttribute('aria-hidden', false); + fadeInElement(this.hintEl_); + this.incrementButton_.disabled = true; + this.decrementButton_.disabled = true; + } + + if (!(this.collateDiv_.hidden = + !this.printTicketStore_.hasCollateCapability() || + !currentValueGreaterThan1)) { + this.collateCheckbox_.checked = + this.printTicketStore_.isCollateEnabled(); + } + + // On Windows, some printers don't specify their duplex values in the + // printer schema. If the printer duplex value is UNKNOWN_DUPLEX_MODE, + // hide the two sided option in preview tab UI. + // Ref bug: http://crbug.com/89204 + if (!(this.duplexDiv_.hidden = + !this.printTicketStore_.hasDuplexCapability())) { + this.duplexCheckbox_.checked = this.printTicketStore_.isDuplexEnabled(); + } + + fadeInOption(this.getElement()); + }, + + /** + * Called when the duplex checkbox changes state. Updates the print ticket. + * @private + */ + onDuplexCheckboxClick_: function() { + this.printTicketStore_.updateDuplex(this.duplexCheckbox_.checked); + }, + + /** + * Called whenever the increment/decrement buttons are clicked. + * @param {number} delta Must be 1 for an increment button click and -1 for + * a decrement button click. + * @private + */ + onButtonClicked_: function(delta) { + // Assumes text field has a valid number. + var newValue = parseInt(this.textfield_.value) + delta; + this.printTicketStore_.updateCopies(newValue); + }, + + /** + * Called after a timeout after user input into the textfield. + * @private + */ + onTextfieldTimeout_: function() { + if (this.textfield_ != '') { + this.printTicketStore_.updateCopies(this.textfield_.value); + } + }, + + /** + * Called when a keyup event occurs on the textfield. Starts an input + * timeout. + * @param {Event} event Contains the pressed key. + * @private + */ + onTextfieldKeyUp_: function(event) { + if (this.textfieldTimeout_) { + clearTimeout(this.textfieldTimeout_); + } + this.textfieldTimeout_ = setTimeout( + this.onTextfieldTimeout_.bind(this), CopiesSettings.TEXTFIELD_DELAY_); + }, + + /** + * Called when the focus leaves the textfield. If the textfield is empty, + * its value is set to 1. + * @private + */ + onTextfieldBlur_: function() { + if (this.textfield_.value == '') { + this.printTicketStore_.updateCopies('1'); + } + }, + + /** + * Called when the collate checkbox is clicked. Updates the print ticket. + * @private + */ + onCollateCheckboxClick_: function() { + this.printTicketStore_.updateCollate(this.collateCheckbox_.checked); + } + }; + + // Export + return { + CopiesSettings: CopiesSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/settings/destination_settings.html b/chrome/browser/resources/print_preview/settings/destination_settings.html new file mode 100644 index 0000000..55a9e47 --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/destination_settings.html @@ -0,0 +1,6 @@ +<div id="destination-settings" class="two-column visible"> + <h1 i18n-content="destinationLabel"></h1> + <div class="right-column"> + <select class="destination-settings-select"></select> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/destination_settings.js b/chrome/browser/resources/print_preview/settings/destination_settings.js new file mode 100644 index 0000000..4713d0a --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/destination_settings.js @@ -0,0 +1,199 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + // TODO(rltoscano): This class needs a throbber while loading the destination + // or another solution is persist the settings of the printer so that next + // load is fast. + + /** + * Component used to render the print destination. + * @param {!print_preview.DestinationStore} destinationStore Used to determine + * the selected destination. + * @constructor + * @extends {print_preview.Component} + */ + function DestinationSettings(destinationStore) { + print_preview.Component.call(this); + + /** + * Used to determine the selected destination. + * @type {!print_preview.DestinationStore} + * @private + */ + this.destinationStore_ = destinationStore; + }; + + /** + * Event types dispatched by the component. + * @enum {string} + */ + DestinationSettings.EventType = { + MANAGE_PRINTERS_SELECT: + 'print_preview.DestinationSettings.MANAGE_PRINTERS_SELECT' + }; + + /** + * CSS classes used by the component. + * @enum {string} + * @private + */ + DestinationSettings.Classes_ = { + SELECT: 'destination-settings-select' + }; + + /** + * Option value of the "Manage Printers..." select option. + * @type {string} + * @const + * @private + */ + DestinationSettings.MANAGE_ID_ = '__manage'; + + DestinationSettings.prototype = { + __proto__: print_preview.Component.prototype, + + set isEnabled(isEnabled) { + this.select_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.select_, 'change', this.onSelectChange_.bind(this)); + this.tracker.add( + this.destinationStore_, + print_preview.DestinationStore.EventType.DESTINATION_SELECT, + this.onDestinationSelect_.bind(this)); + this.tracker.add( + this.destinationStore_, + print_preview.DestinationStore.EventType.DESTINATIONS_INSERTED, + this.onDestinationsInserted_.bind(this)); + }, + + get select_() { + return this.getElement().getElementsByClassName( + DestinationSettings.Classes_.SELECT)[0]; + }, + + renderDestinations_: function() { + var select = this.select_; + select.innerHTML = ''; + var destinations = this.destinationStore_.destinations; + var selectedDestination = this.destinationStore_.selectedDestination; + var saveToPdfDest = null; + var printWithCloudPrintDest = null; + for (var dest, i = 0; dest = destinations[i]; i++) { + if (dest.id == print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) { + saveToPdfDest = dest; + continue; + } + if (dest.isPrintWithCloudPrint) { + printWithCloudPrintDest = dest; + continue; + } + var optionEl = document.createElement('option'); + optionEl.value = dest.id; + optionEl.selected = + selectedDestination && selectedDestination.id == dest.id; + optionEl.textContent = dest.displayName; + select.appendChild(optionEl); + } + + // Add special destinations. + if (saveToPdfDest) { + select.appendChild(this.createSeparatorOption_()); + var printToPdfOptionEl = document.createElement('option'); + printToPdfOptionEl.value = saveToPdfDest.id; + printToPdfOptionEl.selected = + selectedDestination && selectedDestination.id == saveToPdfDest.id; + printToPdfOptionEl.textContent = saveToPdfDest.displayName; + select.appendChild(printToPdfOptionEl); + } + if (printWithCloudPrintDest) { + select.appendChild(this.createSeparatorOption_()); + var printWithCloudPrintOptionEl = document.createElement('option'); + printWithCloudPrintOptionEl.value = printWithCloudPrintDest.id; + printWithCloudPrintOptionEl.selected = + selectedDestination && + selectedDestination.id == printWithCloudPrintDest.id; + printWithCloudPrintOptionEl.textContent = + printWithCloudPrintDest.displayName; + select.appendChild(printWithCloudPrintOptionEl); + } + select.appendChild(this.createSeparatorOption_()); + var manageOptionEl = document.createElement('option'); + manageOptionEl.value = DestinationSettings.MANAGE_ID_; + manageOptionEl.textContent = localStrings.getString('managePrinters'); + select.appendChild(manageOptionEl); + }, + + createSeparatorOption_: function() { + var sep = document.createElement('option'); + sep.disabled = true; + sep.role = 'separator'; + return sep; + }, + + /** + * Called when a destination is selected. Selects the corresponding option. + * @private + */ + onDestinationSelect_: function() { + var select = this.select_; + if (select.options.length > 0) { + select.options[select.selectedIndex].selected = false; + } + var selectedDestination = this.destinationStore_.selectedDestination; + for (var option, i = 0; option = select.options[i]; i++) { + if (selectedDestination.id == option.value) { + option.selected = true; + break; + } + } + }, + + /** + * Called when destinations are inserted into the destination store. Updates + * the select element. + * @private + */ + onDestinationsInserted_: function() { + this.renderDestinations_(); + }, + + /** + * Called when the select element changes options. Selects the corresponding + * print destination. + * @private + */ + onSelectChange_: function() { + var select = this.select_; + var selectedDestId = select.options[select.selectedIndex].value; + + if (selectedDestId == DestinationSettings.MANAGE_ID_) { + cr.dispatchSimpleEvent( + this, DestinationSettings.EventType.MANAGE_PRINTERS_SELECT); + // Select first in the list. + this.destinationStore_.selectDestination( + this.destinationStore_.destinations[0]); + } else { + var destinations = this.destinationStore_.destinations; + for (var dest, i = 0; dest = destinations[i]; i++) { + if (dest.id == selectedDestId) { + this.destinationStore_.selectDestination(dest); + break; + } + } + } + } + }; + + return { + DestinationSettings: DestinationSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/settings/layout_settings.html b/chrome/browser/resources/print_preview/settings/layout_settings.html new file mode 100644 index 0000000..3425e22b --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/layout_settings.html @@ -0,0 +1,18 @@ +<div id="layout-settings" class="two-column visible layout-settings"> + <h1 i18n-content="layoutLabel"></h1> + <div class="right-column"> + <div class="radio"><label> + <input class="layout-settings-portrait-radio" + type="radio" + name="layout" + checked/> + <span i18n-content="optionPortrait"></span> + </label></div> + <div class="radio"><label> + <input class="layout-settings-landscape-radio" + type="radio" + name="layout"/> + <span i18n-content="optionLandscape"></span> + </label></div> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/layout_settings.js b/chrome/browser/resources/print_preview/settings/layout_settings.js new file mode 100644 index 0000000..cba29bf --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/layout_settings.js @@ -0,0 +1,124 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a LayoutSettings object. This object encapsulates all settings and + * logic related to layout mode (portrait/landscape). + * @param {!print_preview.PrintTicketStore} printTicketStore Used to get the + * layout written to the print ticket. + * @constructor + * @extends {print_preview.Component} + */ + function LayoutSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used to get the layout written to the print ticket. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + }; + + /** + * CSS classes used by the layout settings. + * @enum {string} + * @private + */ + LayoutSettings.Classes_ = { + LANDSCAPE_RADIO: 'layout-settings-landscape-radio', + PORTRAIT_RADIO: 'layout-settings-portrait-radio' + }; + + LayoutSettings.prototype = { + __proto__: print_preview.Component.prototype, + + /** @param {boolean} isEnabled Whether this component is enabled. */ + set isEnabled(isEnabled) { + this.landscapeRadioButton_.disabled = !isEnabled; + this.portraitRadioButton_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.portraitRadioButton_, + 'click', + this.onLayoutButtonClick_.bind(this)); + this.tracker.add( + this.landscapeRadioButton_, + 'click', + this.onLayoutButtonClick_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onPrintTicketStoreChange_.bind(this)); + }, + + /** + * @return {HTMLInputElement} The portrait orientation radio button. + * @private + */ + get portraitRadioButton_() { + return this.getElement().getElementsByClassName( + LayoutSettings.Classes_.PORTRAIT_RADIO)[0]; + }, + + /** + * @return {HTMLInputElement} The landscape orientation radio button. + * @private + */ + get landscapeRadioButton_() { + return this.getElement().getElementsByClassName( + LayoutSettings.Classes_.LANDSCAPE_RADIO)[0]; + }, + + /** + * Called when one of the radio buttons is clicked. Updates the print ticket + * store. + * @private + */ + onLayoutButtonClick_: function() { + this.printTicketStore_.updateOrientation( + this.landscapeRadioButton_.checked); + }, + + /** + * Called when the print ticket store changes state. Updates the state of + * the radio buttons and hides the setting if necessary. + * @private + */ + onPrintTicketStoreChange_: function() { + if (this.printTicketStore_.hasOrientationCapability()) { + var isLandscapeEnabled = this.printTicketStore_.isLandscapeEnabled(); + this.portraitRadioButton_.checked = !isLandscapeEnabled; + this.landscapeRadioButton_.checked = isLandscapeEnabled; + fadeInOption(this.getElement()); + } else { + fadeOutOption(this.getElement()); + } + } + }; + + // Export + return { + LayoutSettings: LayoutSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/margin_settings.html b/chrome/browser/resources/print_preview/settings/margin_settings.html index 80d98d87..b3faaa3 100644 --- a/chrome/browser/resources/print_preview/margin_settings.html +++ b/chrome/browser/resources/print_preview/settings/margin_settings.html @@ -1,7 +1,9 @@ -<div id="margins-option" class="two-column visible"> +<div id="margin-settings" class="two-column visible margin-settings"> <h1 i18n-content="marginsLabel"></h1> <div class="right-column"> - <select id="margin-list"> + <select class="margin-settings-select"> + <!-- The order of these options must match the natural order of their + values, which come from print_preview.ticket_items.MarginsType.Value. --> <option i18n-content="defaultMargins" value="0" selected></option> <option i18n-content="noMargins" value="1"></option> <option i18n-content="minimumMargins" value="2"></option> diff --git a/chrome/browser/resources/print_preview/settings/margin_settings.js b/chrome/browser/resources/print_preview/settings/margin_settings.js new file mode 100644 index 0000000..54e5699 --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/margin_settings.js @@ -0,0 +1,112 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a MarginSettings object. This object encapsulates all settings and + * logic related to the margins mode. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to get + * ticket margins value. + * @constructor + * @extends {print_preview.Component} + */ + function MarginSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used to get ticket margins value. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + }; + + /** + * CSS classes used by the margin settings component. + * @enum {string} + * @private + */ + MarginSettings.Classes_ = { + SELECT: 'margin-settings-select' + }; + + MarginSettings.prototype = { + __proto__: print_preview.Component.prototype, + + /** @param {boolean} isEnabled Whether this component is enabled. */ + set isEnabled(isEnabled) { + this.select_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.select_, 'change', this.onSelectChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + }, + + /** + * @return {HTMLSelectElement} Select element containing the margin options. + * @private + */ + get select_() { + return this.getElement().getElementsByClassName( + MarginSettings.Classes_.SELECT)[0]; + }, + + /** + * Called when the select element is changed. Updates the print ticket + * margin type. + * @private + */ + onSelectChange_: function() { + var select = this.select_; + var marginsType = + /** @type {print_preview.ticket_items.MarginsType.Value} */ ( + select.selectedIndex); + this.printTicketStore_.updateMarginsType(marginsType); + }, + + /** + * Called when the print ticket store changes. Selects the corresponding + * select option. + * @private + */ + onPrintTicketStoreChange_: function() { + if (this.printTicketStore_.hasMarginsCapability()) { + var select = this.select_; + var marginsType = this.printTicketStore_.getMarginsType(); + var selectedMarginsType = + /** @type {print_preview.ticket_items.MarginsType.Value} */ ( + select.selectedIndex); + if (marginsType != selectedMarginsType) { + select.options[selectedMarginsType].selected = false; + select.options[marginsType].selected = true; + } + fadeInOption(this.getElement()); + } else { + fadeOutOption(this.getElement()); + } + } + }; + + // Export + return { + MarginSettings: MarginSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/settings/other_options_settings.html b/chrome/browser/resources/print_preview/settings/other_options_settings.html new file mode 100644 index 0000000..623dfa0 --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/other_options_settings.html @@ -0,0 +1,20 @@ +<div id="other-options-settings" + class="other-options-settings two-column visible"> + <h1 i18n-content="optionsLabel"></h1> + <div class="right-column checkbox"> + <div class="other-options-settings-header-footer"> + <label> + <input class="other-options-settings-header-footer-checkbox" + type="checkbox" /> + <span i18n-content="optionHeaderFooter"></span> + </label> + </div> + <div class="other-options-settings-fit-to-page"> + <label> + <input class="other-options-settings-fit-to-page-checkbox" + type="checkbox" /> + <span i18n-content="optionFitToPage"></span> + </label> + </div> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/other_options_settings.js b/chrome/browser/resources/print_preview/settings/other_options_settings.js new file mode 100644 index 0000000..621b62b --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/other_options_settings.js @@ -0,0 +1,172 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * UI component that renders checkboxes for various print options. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to monitor + * the state of the print ticket. + * @constructor + * @extends {print_preview.Component} + */ + function OtherOptionsSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used to monitor the state of the print ticket. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Header footer container element. + * @type {HTMLElement} + * @private + */ + this.headerFooterEl_ = null; + + /** + * Header footer checkbox. + * @type {HTMLInputElement} + * @private + */ + this.headerFooterCheckbox_ = null; + + /** + * Fit-to-page container element. + * @type {HTMLElement} + * @private + */ + this.fitToPageEl_ = null; + + /** + * Fit-to-page checkbox. + * @type {HTMLInputElement} + * @private + */ + this.fitToPageCheckbox_ = null; + }; + + /** + * CSS classes used by the other options component. + * @enum {string} + * @private + */ + OtherOptionsSettings.Classes_ = { + FIT_TO_PAGE: 'other-options-settings-fit-to-page', + FIT_TO_PAGE_CHECKBOX: 'other-options-settings-fit-to-page-checkbox', + HEADER_FOOTER: 'other-options-settings-header-footer', + HEADER_FOOTER_CHECKBOX: 'other-options-settings-header-footer-checkbox' + }; + + OtherOptionsSettings.prototype = { + __proto__: print_preview.Component.prototype, + + set isEnabled(isEnabled) { + this.headerFooterCheckbox_.disabled = !isEnabled; + this.fitToPageCheckbox_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.headerFooterCheckbox_, + 'click', + this.onHeaderFooterCheckboxClick_.bind(this)); + this.tracker.add( + this.fitToPageCheckbox_, + 'click', + this.onFitToPageCheckboxClick_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.INITIALIZE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + }, + + /** @override */ + exitDocument: function() { + print_preview.Component.prototype.exitDocument.call(this); + this.headerFooterEl_ = null; + this.headerFooterCheckbox_ = null; + this.fitToPageEl_ = null; + this.fitToPageCheckbox_ = null; + }, + + /** @override */ + decorateInternal: function() { + this.headerFooterEl_ = this.getElement().getElementsByClassName( + OtherOptionsSettings.Classes_.HEADER_FOOTER)[0]; + this.headerFooterCheckbox_ = this.getElement().getElementsByClassName( + OtherOptionsSettings.Classes_.HEADER_FOOTER_CHECKBOX)[0]; + this.fitToPageEl_ = this.getElement().getElementsByClassName( + OtherOptionsSettings.Classes_.FIT_TO_PAGE)[0]; + this.fitToPageCheckbox_ = this.getElement().getElementsByClassName( + OtherOptionsSettings.Classes_.FIT_TO_PAGE_CHECKBOX)[0]; + }, + + /** + * Called when the header-footer checkbox is clicked. Updates the print + * ticket. + * @private + */ + onHeaderFooterCheckboxClick_: function() { + this.printTicketStore_.updateHeaderFooter( + this.headerFooterCheckbox_.checked); + }, + + /** + * Called when the fit-to-page checkbox is clicked. Updates the print + * ticket. + * @private + */ + onFitToPageCheckboxClick_: function() { + this.printTicketStore_.updateFitToPage( + this.fitToPageCheckbox_.checked); + }, + + /** + * Called when the print ticket store has changed. Hides or shows the + * setting. + * @private + */ + onPrintTicketStoreChange_: function() { + setIsVisible(this.headerFooterEl_, + this.printTicketStore_.hasHeaderFooterCapability()); + this.headerFooterCheckbox_.checked = + this.printTicketStore_.isHeaderFooterEnabled(); + setIsVisible(this.fitToPageEl_, + this.printTicketStore_.hasFitToPageCapability()); + this.fitToPageCheckbox_.checked = + this.printTicketStore_.isFitToPageEnabled(); + + if (this.printTicketStore_.hasHeaderFooterCapability() || + this.printTicketStore_.hasFitToPageCapability()) { + fadeInOption(this.getElement()); + } else { + fadeOutOption(this.getElement()); + } + } + }; + + // Export + return { + OtherOptionsSettings: OtherOptionsSettings + }; +}); diff --git a/chrome/browser/resources/print_preview/settings/page_settings.css b/chrome/browser/resources/print_preview/settings/page_settings.css new file mode 100644 index 0000000..cabdf7f --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/page_settings.css @@ -0,0 +1,13 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#page-settings .page-settings-custom-input.invalid { + background: rgb(255, 240, 240); + color: rgb(140, 20, 20); +} + +#page-settings .page-settings-print-pages-div { + white-space: nowrap; +} diff --git a/chrome/browser/resources/print_preview/settings/page_settings.html b/chrome/browser/resources/print_preview/settings/page_settings.html new file mode 100644 index 0000000..76a8aef --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/page_settings.html @@ -0,0 +1,23 @@ +<div id="page-settings" class="two-column visible page-settings"> + <h1 i18n-content="pagesLabel"></h1> + <div class="right-column"> + <div class="radio"><label> + <input class="page-settings-all-radio" name="pages" checked type="radio"> + <span i18n-content="optionAllPages"></span> + </label></div> + <div> + <div class="page-settings-print-pages-div"> + <input class="page-settings-custom-radio" + name="pages" + type="radio" + i18n-values="aria-label:printPagesLabel;"/> + <input class="page-settings-custom-input" + type="text" + i18n-values="placeholder:examplePageRangeText"/> + </div> + <span class="page-settings-custom-hint hint suggestion" + aria-hidden="true" + aria-live="polite"></span> + </div> + </div> +</div> diff --git a/chrome/browser/resources/print_preview/settings/page_settings.js b/chrome/browser/resources/print_preview/settings/page_settings.js new file mode 100644 index 0000000..1489c6b --- /dev/null +++ b/chrome/browser/resources/print_preview/settings/page_settings.js @@ -0,0 +1,247 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('print_preview', function() { + 'use strict'; + + /** + * Creates a PageSettings object. This object encapsulates all settings and + * logic related to page selection. + * @param {!print_preview.PrintTicketStore} printTicketStore Used to read and + * write page range settings. + * @constructor + * @extends {print_preview.Component} + */ + function PageSettings(printTicketStore) { + print_preview.Component.call(this); + + /** + * Used to read and write page range settings. + * @type {!print_preview.PrintTicketStore} + * @private + */ + this.printTicketStore_ = printTicketStore; + + /** + * Timeout used to delay processing of the custom page range input. + * @type {Object} + * @private + */ + this.customInputTimeout_ = null; + + /** + * Custom page range input. + * @type {HTMLInputElement} + * @private + */ + this.customInput_ = null; + + /** + * Custom page range radio button. + * @type {HTMLInputElement} + * @private + */ + this.customRadio_ = null; + + /** + * All page rage radio button. + * @type {HTMLInputElement} + * @private + */ + this.allRadio_ = null; + + /** + * Container of a hint to show when the custom page range is invalid. + * @type {HTMLElement} + * @private + */ + this.customHintEl_ = null; + }; + + /** + * CSS classes used by the page settings. + * @enum {string} + * @private + */ + PageSettings.Classes_ = { + ALL_RADIO: 'page-settings-all-radio', + CUSTOM_HINT: 'page-settings-custom-hint', + CUSTOM_INPUT: 'page-settings-custom-input', + CUSTOM_RADIO: 'page-settings-custom-radio' + }; + + /** + * Delay in milliseconds before processing custom page range input. + * @type {number} + * @private + */ + PageSettings.CUSTOM_INPUT_DELAY_ = 500; + + PageSettings.prototype = { + __proto__: print_preview.Component.prototype, + + set isEnabled(isEnabled) { + this.customInput_.disabled = !isEnabled; + this.allRadio_.disabled = !isEnabled; + this.customRadio_.disabled = !isEnabled; + }, + + /** @override */ + enterDocument: function() { + print_preview.Component.prototype.enterDocument.call(this); + this.tracker.add( + this.allRadio_, 'click', this.onAllRadioClick_.bind(this)); + this.tracker.add( + this.customRadio_, 'click', this.onCustomRadioClick_.bind(this)); + this.tracker.add( + this.customInput_, 'blur', this.onCustomInputBlur_.bind(this)); + this.tracker.add( + this.customInput_, 'keyup', this.onCustomInputKeyUp_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.DOCUMENT_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.TICKET_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + this.tracker.add( + this.printTicketStore_, + print_preview.PrintTicketStore.EventType.CAPABILITIES_CHANGE, + this.onPrintTicketStoreChange_.bind(this)); + }, + + /** @override */ + exitDocument: function() { + print_preview.Component.prototype.exitDocument.call(this); + this.customInput_ = null; + this.customRadio_ = null; + this.allRadio_ = null; + this.customHintEl_ = null; + }, + + /** @override */ + decorateInternal: function() { + this.customInput_ = this.getElement().getElementsByClassName( + PageSettings.Classes_.CUSTOM_INPUT)[0]; + this.allRadio_ = this.getElement().getElementsByClassName( + PageSettings.Classes_.ALL_RADIO)[0]; + this.customRadio_ = this.getElement().getElementsByClassName( + PageSettings.Classes_.CUSTOM_RADIO)[0]; + this.customHintEl_ = this.getElement().getElementsByClassName( + PageSettings.Classes_.CUSTOM_HINT)[0]; + this.customHintEl_.textContent = localStrings.getStringF( + 'pageRangeInstruction', + localStrings.getString('examplePageRangeText')); + }, + + /** + * @param {boolean} Whether the custom hint is visible. + * @private + */ + setInvalidStateVisible_: function(isVisible) { + if (isVisible) { + this.customInput_.classList.add('invalid'); + this.customHintEl_.setAttribute('aria-hidden', 'false'); + fadeInElement(this.customHintEl_); + } else { + this.customInput_.classList.remove('invalid'); + fadeOutElement(this.customHintEl_); + this.customHintEl_.setAttribute('aria-hidden', 'true'); + } + }, + + /** + * Called when the all radio button is clicked. Updates the print ticket. + * @private + */ + onAllRadioClick_: function() { + this.printTicketStore_.updatePageRange(''); + }, + + /** + * Called when the custom radio button is clicked. Updates the print ticket. + * @private + */ + onCustomRadioClick_: function() { + this.customInput_.focus(); + this.printTicketStore_.updatePageRange(this.customInput_.value); + }, + + /** + * Called when the custom input is blurred. Enables the all radio button if + * the custom input is empty. + * @private + */ + onCustomInputBlur_: function() { + if (this.customInput_.value == '') { + this.allRadio_.checked = true; + this.customRadio_.checked = false; + } + }, + + /** + * Called when a key is pressed on the custom input. + * @param {Event} event Contains the key that was pressed. + * @private + */ + onCustomInputKeyUp_: function(event) { + if (this.customInputTimeout_) { + clearTimeout(this.customInputTimeout_); + } + if (event.keyIdentifier == 'Enter') { + this.printTicketStore_.updatePageRange(this.customInput_.value); + } else { + this.allRadio_.checked = false; + this.customRadio_.checked = true; + this.customInputTimeout_ = setTimeout( + this.onCustomInputTimeout_.bind(this), + PageSettings.CUSTOM_INPUT_DELAY_); + } + }, + + /** + * Called after a delay following a key press in the custom input. + * @private + */ + onCustomInputTimeout_: function() { + this.customInputTimeout_ = null; + if (this.customRadio_.checked) { + this.printTicketStore_.updatePageRange(this.customInput_.value); + } + }, + + /** + * Called when the print ticket changes. Updates the state of the component. + * @private + */ + onPrintTicketStoreChange_: function() { + if (this.printTicketStore_.hasPageRangeCapability()) { + var pageRangeStr = this.printTicketStore_.getPageRangeStr(); + if (pageRangeStr || this.customRadio_.checked) { + if (!document.hasFocus() || + document.activeElement != this.customInput_) { + this.customInput_.value = pageRangeStr; + } + this.customRadio_.checked = true; + this.allRadio_.checked = false; + this.setInvalidStateVisible_( + !this.printTicketStore_.isPageRangeValid()); + } else { + this.allRadio_.checked = true; + this.customRadio_.checked = false; + this.setInvalidStateVisible_(false); + } + fadeInOption(this.getElement()); + } else { + fadeOutOption(this.getElement()); + } + } + }; + + // Export + return { + PageSettings: PageSettings + }; +}); |