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