1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 cr.define('print_preview', function() {
8 // TODO(rltoscano): Maybe clear print ticket when destination changes. Or
9 // better yet, carry over any print ticket state that is possible. I.e. if
10 // destination changes, the new destination might not support duplex anymore,
11 // so we should clear the ticket's isDuplexEnabled state.
14 * Storage of the print ticket and document statistics. Dispatches events when
15 * the contents of the print ticket or document statistics change. Also
16 * handles validation of the print ticket against destination capabilities and
17 * against the document.
18 * @param {!print_preview.DestinationStore} destinationStore Used to
19 * understand which printer is selected.
20 * @param {!print_preview.AppState} appState Print preview application state.
21 * @param {!print_preview.DocumentInfo} documentInfo Document data model.
23 * @extends {cr.EventTarget}
25 function PrintTicketStore(destinationStore, appState, documentInfo) {
26 cr.EventTarget.call(this);
29 * Destination store used to understand which printer is selected.
30 * @type {!print_preview.DestinationStore}
33 this.destinationStore_ = destinationStore;
36 * App state used to persist and load ticket values.
37 * @type {!print_preview.AppState}
40 this.appState_ = appState;
43 * Information about the document to print.
44 * @type {!print_preview.DocumentInfo}
47 this.documentInfo_ = documentInfo;
50 * Printing capabilities of Chromium and the currently selected destination.
51 * @type {!print_preview.CapabilitiesHolder}
54 this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder();
57 * Current measurement system. Used to work with margin measurements.
58 * @type {!print_preview.MeasurementSystem}
61 this.measurementSystem_ = new print_preview.MeasurementSystem(
62 ',', '.', print_preview.MeasurementSystem.UnitType.IMPERIAL);
65 * Collate ticket item.
66 * @type {!print_preview.ticket_items.Collate}
69 this.collate_ = new print_preview.ticket_items.Collate(
70 this.appState_, this.destinationStore_);
74 * @type {!print_preview.ticket_items.Color}
77 this.color_ = new print_preview.ticket_items.Color(
78 this.appState_, this.destinationStore_);
82 * @type {!print_preview.ticket_items.Copies}
86 new print_preview.ticket_items.Copies(this.destinationStore_);
90 * @type {!print_preview.ticket_items.Dpi}
93 this.dpi_ = new print_preview.ticket_items.Dpi(
94 this.appState_, this.destinationStore_);
98 * @type {!print_preview.ticket_items.Duplex}
101 this.duplex_ = new print_preview.ticket_items.Duplex(
102 this.appState_, this.destinationStore_);
105 * Page range ticket item.
106 * @type {!print_preview.ticket_items.PageRange}
110 new print_preview.ticket_items.PageRange(this.documentInfo_);
113 * Custom margins ticket item.
114 * @type {!print_preview.ticket_items.CustomMargins}
117 this.customMargins_ = new print_preview.ticket_items.CustomMargins(
118 this.appState_, this.documentInfo_);
121 * Margins type ticket item.
122 * @type {!print_preview.ticket_items.MarginsType}
125 this.marginsType_ = new print_preview.ticket_items.MarginsType(
126 this.appState_, this.documentInfo_, this.customMargins_);
129 * Media size ticket item.
130 * @type {!print_preview.ticket_items.MediaSize}
133 this.mediaSize_ = new print_preview.ticket_items.MediaSize(
135 this.destinationStore_,
138 this.customMargins_);
141 * Landscape ticket item.
142 * @type {!print_preview.ticket_items.Landscape}
145 this.landscape_ = new print_preview.ticket_items.Landscape(
147 this.destinationStore_,
150 this.customMargins_);
153 * Header-footer ticket item.
154 * @type {!print_preview.ticket_items.HeaderFooter}
157 this.headerFooter_ = new print_preview.ticket_items.HeaderFooter(
161 this.customMargins_);
164 * Fit-to-page ticket item.
165 * @type {!print_preview.ticket_items.FitToPage}
168 this.fitToPage_ = new print_preview.ticket_items.FitToPage(
169 this.documentInfo_, this.destinationStore_);
172 * Print CSS backgrounds ticket item.
173 * @type {!print_preview.ticket_items.CssBackground}
176 this.cssBackground_ = new print_preview.ticket_items.CssBackground(
177 this.appState_, this.documentInfo_);
180 * Print selection only ticket item.
181 * @type {!print_preview.ticket_items.SelectionOnly}
184 this.selectionOnly_ =
185 new print_preview.ticket_items.SelectionOnly(this.documentInfo_);
188 * Print friendly ticket item.
189 * @type {!print_preview.ticket_items.DistillPage}
192 this.distillPage_ = new print_preview.ticket_items.DistillPage(
196 * Vendor ticket items.
197 * @type {!print_preview.ticket_items.VendorItems}
200 this.vendorItems_ = new print_preview.ticket_items.VendorItems(
201 this.appState_, this.destinationStore_);
204 * Keeps track of event listeners for the print ticket store.
205 * @type {!EventTracker}
208 this.tracker_ = new EventTracker();
211 * Whether the print preview has been initialized.
215 this.isInitialized_ = false;
217 this.addEventListeners_();
221 * Event types dispatched by the print ticket store.
224 PrintTicketStore.EventType = {
225 CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE',
226 DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE',
227 INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE',
228 TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE'
231 PrintTicketStore.prototype = {
232 __proto__: cr.EventTarget.prototype,
235 * Whether the print preview has been initialized.
238 get isInitialized() {
239 return this.isInitialized_;
243 return this.collate_;
254 get cssBackground() {
255 return this.cssBackground_;
258 get customMargins() {
259 return this.customMargins_;
271 return this.fitToPage_;
275 return this.headerFooter_;
279 return this.distillPage_;
283 return this.mediaSize_;
287 return this.landscape_;
291 return this.marginsType_;
295 return this.pageRange_;
298 get selectionOnly() {
299 return this.selectionOnly_;
303 return this.vendorItems_;
307 * @return {!print_preview.MeasurementSystem} Measurement system of the
310 get measurementSystem() {
311 return this.measurementSystem_;
315 * Initializes the print ticket store. Dispatches an INITIALIZE event.
316 * @param {string} thousandsDelimeter Delimeter of the thousands place.
317 * @param {string} decimalDelimeter Delimeter of the decimal point.
318 * @param {!print_preview.MeasurementSystem.UnitType} unitType Type of unit
319 * of the local measurement system.
320 * @param {boolean} selectionOnly Whether only selected content should be
324 thousandsDelimeter, decimalDelimeter, unitType, selectionOnly) {
325 this.measurementSystem_.setSystem(thousandsDelimeter, decimalDelimeter,
327 this.selectionOnly_.updateValue(selectionOnly);
329 // Initialize ticket with user's previous values.
330 if (this.appState_.hasField(
331 print_preview.AppState.Field.IS_COLOR_ENABLED)) {
332 this.color_.updateValue(
333 /** @type {!Object} */(this.appState_.getField(
334 print_preview.AppState.Field.IS_COLOR_ENABLED)));
336 if (this.appState_.hasField(print_preview.AppState.Field.DPI)) {
337 this.dpi_.updateValue(
338 /** @type {!Object} */(this.appState_.getField(
339 print_preview.AppState.Field.DPI)));
341 if (this.appState_.hasField(
342 print_preview.AppState.Field.IS_DUPLEX_ENABLED)) {
343 this.duplex_.updateValue(
344 /** @type {!Object} */(this.appState_.getField(
345 print_preview.AppState.Field.IS_DUPLEX_ENABLED)));
347 if (this.appState_.hasField(print_preview.AppState.Field.MEDIA_SIZE)) {
348 this.mediaSize_.updateValue(
349 /** @type {!Object} */(this.appState_.getField(
350 print_preview.AppState.Field.MEDIA_SIZE)));
352 if (this.appState_.hasField(
353 print_preview.AppState.Field.IS_LANDSCAPE_ENABLED)) {
354 this.landscape_.updateValue(
355 /** @type {!Object} */(this.appState_.getField(
356 print_preview.AppState.Field.IS_LANDSCAPE_ENABLED)));
358 // Initialize margins after landscape because landscape may reset margins.
359 if (this.appState_.hasField(print_preview.AppState.Field.MARGINS_TYPE)) {
360 this.marginsType_.updateValue(
361 /** @type {!Object} */(this.appState_.getField(
362 print_preview.AppState.Field.MARGINS_TYPE)));
364 if (this.appState_.hasField(
365 print_preview.AppState.Field.CUSTOM_MARGINS)) {
366 this.customMargins_.updateValue(
367 /** @type {!Object} */(this.appState_.getField(
368 print_preview.AppState.Field.CUSTOM_MARGINS)));
370 if (this.appState_.hasField(
371 print_preview.AppState.Field.IS_HEADER_FOOTER_ENABLED)) {
372 this.headerFooter_.updateValue(
373 /** @type {!Object} */(this.appState_.getField(
374 print_preview.AppState.Field.IS_HEADER_FOOTER_ENABLED)));
376 if (this.appState_.hasField(
377 print_preview.AppState.Field.IS_COLLATE_ENABLED)) {
378 this.collate_.updateValue(
379 /** @type {!Object} */(this.appState_.getField(
380 print_preview.AppState.Field.IS_COLLATE_ENABLED)));
382 if (this.appState_.hasField(
383 print_preview.AppState.Field.IS_CSS_BACKGROUND_ENABLED)) {
384 this.cssBackground_.updateValue(
385 /** @type {!Object} */(this.appState_.getField(
386 print_preview.AppState.Field.IS_CSS_BACKGROUND_ENABLED)));
388 if (this.appState_.hasField(
389 print_preview.AppState.Field.VENDOR_OPTIONS)) {
390 this.vendorItems_.updateValue(
391 /** @type {!Object<string>} */(this.appState_.getField(
392 print_preview.AppState.Field.VENDOR_OPTIONS)));
397 * @return {boolean} {@code true} if the stored print ticket is valid,
398 * {@code false} otherwise.
400 isTicketValid: function() {
401 return this.isTicketValidForPreview() &&
402 (!this.copies_.isCapabilityAvailable() || this.copies_.isValid()) &&
403 (!this.pageRange_.isCapabilityAvailable() ||
404 this.pageRange_.isValid());
407 /** @return {boolean} Whether the ticket is valid for preview generation. */
408 isTicketValidForPreview: function() {
409 return (!this.marginsType_.isCapabilityAvailable() ||
410 !this.marginsType_.isValueEqual(
411 print_preview.ticket_items.MarginsType.Value.CUSTOM) ||
412 this.customMargins_.isValid());
416 * Creates an object that represents a Google Cloud Print print ticket.
417 * @param {!print_preview.Destination} destination Destination to print to.
418 * @return {!Object} Google Cloud Print print ticket.
420 createPrintTicket: function(destination) {
421 assert(!destination.isLocal ||
422 destination.isPrivet || destination.isExtension,
423 'Trying to create a Google Cloud Print print ticket for a local ' +
424 ' non-privet and non-extension destination');
426 assert(destination.capabilities,
427 'Trying to create a Google Cloud Print print ticket for a ' +
428 'destination with no print capabilities');
433 if (this.collate.isCapabilityAvailable() && this.collate.isUserEdited()) {
434 cjt.print.collate = {collate: this.collate.getValue()};
436 if (this.color.isCapabilityAvailable() && this.color.isUserEdited()) {
437 var selectedOption = this.color.getSelectedOption();
438 if (!selectedOption) {
439 console.error('Could not find correct color option');
441 cjt.print.color = {type: selectedOption.type};
442 if (selectedOption.hasOwnProperty('vendor_id')) {
443 cjt.print.color.vendor_id = selectedOption.vendor_id;
447 if (this.copies.isCapabilityAvailable() && this.copies.isUserEdited()) {
448 cjt.print.copies = {copies: this.copies.getValueAsNumber()};
450 if (this.duplex.isCapabilityAvailable() && this.duplex.isUserEdited()) {
452 {type: this.duplex.getValue() ? 'LONG_EDGE' : 'NO_DUPLEX'};
454 if (this.mediaSize.isCapabilityAvailable()) {
455 var value = this.mediaSize.getValue();
456 cjt.print.media_size = {
457 width_microns: value.width_microns,
458 height_microns: value.height_microns,
459 is_continuous_feed: value.is_continuous_feed,
460 vendor_id: value.vendor_id
463 if (!this.landscape.isCapabilityAvailable()) {
464 // In this case "orientation" option is hidden from user, so user can't
465 // adjust it for page content, see Landscape.isCapabilityAvailable().
466 // We can improve results if we set AUTO here.
467 if (this.landscape.hasOption('AUTO'))
468 cjt.print.page_orientation = { type: 'AUTO' };
469 } else if (this.landscape.isUserEdited()) {
470 cjt.print.page_orientation =
471 {type: this.landscape.getValue() ? 'LANDSCAPE' : 'PORTRAIT'};
473 if (this.dpi.isCapabilityAvailable()) {
474 var value = this.dpi.getValue();
476 horizontal_dpi: value.horizontal_dpi,
477 vertical_dpi: value.vertical_dpi,
478 vendor_id: value.vendor_id
481 if (this.vendorItems.isCapabilityAvailable() &&
482 this.vendorItems.isUserEdited()) {
483 var items = this.vendorItems.ticketItems;
484 cjt.print.vendor_ticket_item = [];
485 for (var itemId in items) {
486 if (items.hasOwnProperty(itemId)) {
487 cjt.print.vendor_ticket_item.push(
488 {id: itemId, value: items[itemId]});
492 return JSON.stringify(cjt);
496 * Adds event listeners for the print ticket store.
499 addEventListeners_: function() {
501 this.destinationStore_,
502 print_preview.DestinationStore.EventType.DESTINATION_SELECT,
503 this.onDestinationSelect_.bind(this));
506 this.destinationStore_,
507 print_preview.DestinationStore.EventType.
508 SELECTED_DESTINATION_CAPABILITIES_READY,
509 this.onSelectedDestinationCapabilitiesReady_.bind(this));
512 this.destinationStore_,
513 print_preview.DestinationStore.EventType.
514 CACHED_SELECTED_DESTINATION_INFO_READY,
515 this.onSelectedDestinationCapabilitiesReady_.bind(this));
517 // TODO(rltoscano): Print ticket store shouldn't be re-dispatching these
518 // events, the consumers of the print ticket store events should listen
519 // for the events from document info instead. Will move this when
520 // consumers are all migrated.
523 print_preview.DocumentInfo.EventType.CHANGE,
524 this.onDocumentInfoChange_.bind(this));
528 * Called when the destination selected.
531 onDestinationSelect_: function() {
532 // Reset user selection for certain ticket items.
533 if (this.capabilitiesHolder_.get() != null) {
534 this.customMargins_.updateValue(null);
535 if (this.marginsType_.getValue() ==
536 print_preview.ticket_items.MarginsType.Value.CUSTOM) {
537 this.marginsType_.updateValue(
538 print_preview.ticket_items.MarginsType.Value.DEFAULT);
540 this.vendorItems_.updateValue({});
545 * Called when the capabilities of the selected destination are ready.
548 onSelectedDestinationCapabilitiesReady_: function() {
550 this.destinationStore_.selectedDestination.capabilities);
551 var isFirstUpdate = this.capabilitiesHolder_.get() == null;
552 this.capabilitiesHolder_.set(caps);
554 this.isInitialized_ = true;
555 cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE);
557 cr.dispatchSimpleEvent(
558 this, PrintTicketStore.EventType.CAPABILITIES_CHANGE);
563 * Called when document data model has changed. Dispatches a print ticket
567 onDocumentInfoChange_: function() {
568 cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
574 PrintTicketStore: PrintTicketStore