Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / data / print_ticket_store.js
blobe063bda76671914ab605757422e02b330c1feb5d
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() {
6   'use strict';
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.
13   /**
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.
22    * @constructor
23    * @extends {cr.EventTarget}
24    */
25   function PrintTicketStore(destinationStore, appState, documentInfo) {
26     cr.EventTarget.call(this);
28     /**
29      * Destination store used to understand which printer is selected.
30      * @type {!print_preview.DestinationStore}
31      * @private
32      */
33     this.destinationStore_ = destinationStore;
35     /**
36      * App state used to persist and load ticket values.
37      * @type {!print_preview.AppState}
38      * @private
39      */
40     this.appState_ = appState;
42     /**
43      * Information about the document to print.
44      * @type {!print_preview.DocumentInfo}
45      * @private
46      */
47     this.documentInfo_ = documentInfo;
49     /**
50      * Printing capabilities of Chromium and the currently selected destination.
51      * @type {!print_preview.CapabilitiesHolder}
52      * @private
53      */
54     this.capabilitiesHolder_ = new print_preview.CapabilitiesHolder();
56     /**
57      * Current measurement system. Used to work with margin measurements.
58      * @type {!print_preview.MeasurementSystem}
59      * @private
60      */
61     this.measurementSystem_ = new print_preview.MeasurementSystem(
62         ',', '.', print_preview.MeasurementSystem.UnitType.IMPERIAL);
64     /**
65      * Collate ticket item.
66      * @type {!print_preview.ticket_items.Collate}
67      * @private
68      */
69     this.collate_ = new print_preview.ticket_items.Collate(
70         this.appState_, this.destinationStore_);
72     /**
73      * Color ticket item.
74      * @type {!print_preview.ticket_items.Color}
75      * @private
76      */
77     this.color_ = new print_preview.ticket_items.Color(
78         this.appState_, this.destinationStore_);
80     /**
81      * Copies ticket item.
82      * @type {!print_preview.ticket_items.Copies}
83      * @private
84      */
85     this.copies_ =
86         new print_preview.ticket_items.Copies(this.destinationStore_);
88     /**
89      * DPI ticket item.
90      * @type {!print_preview.ticket_items.Dpi}
91      * @private
92      */
93     this.dpi_ = new print_preview.ticket_items.Dpi(
94         this.appState_, this.destinationStore_);
96     /**
97      * Duplex ticket item.
98      * @type {!print_preview.ticket_items.Duplex}
99      * @private
100      */
101     this.duplex_ = new print_preview.ticket_items.Duplex(
102         this.appState_, this.destinationStore_);
104     /**
105      * Page range ticket item.
106      * @type {!print_preview.ticket_items.PageRange}
107      * @private
108      */
109     this.pageRange_ =
110         new print_preview.ticket_items.PageRange(this.documentInfo_);
112     /**
113      * Custom margins ticket item.
114      * @type {!print_preview.ticket_items.CustomMargins}
115      * @private
116      */
117     this.customMargins_ = new print_preview.ticket_items.CustomMargins(
118         this.appState_, this.documentInfo_);
120     /**
121      * Margins type ticket item.
122      * @type {!print_preview.ticket_items.MarginsType}
123      * @private
124      */
125     this.marginsType_ = new print_preview.ticket_items.MarginsType(
126         this.appState_, this.documentInfo_, this.customMargins_);
128     /**
129      * Media size ticket item.
130      * @type {!print_preview.ticket_items.MediaSize}
131      * @private
132      */
133     this.mediaSize_ = new print_preview.ticket_items.MediaSize(
134         this.appState_,
135         this.destinationStore_,
136         this.documentInfo_,
137         this.marginsType_,
138         this.customMargins_);
140     /**
141      * Landscape ticket item.
142      * @type {!print_preview.ticket_items.Landscape}
143      * @private
144      */
145     this.landscape_ = new print_preview.ticket_items.Landscape(
146         this.appState_,
147         this.destinationStore_,
148         this.documentInfo_,
149         this.marginsType_,
150         this.customMargins_);
152     /**
153      * Header-footer ticket item.
154      * @type {!print_preview.ticket_items.HeaderFooter}
155      * @private
156      */
157     this.headerFooter_ = new print_preview.ticket_items.HeaderFooter(
158         this.appState_,
159         this.documentInfo_,
160         this.marginsType_,
161         this.customMargins_);
163     /**
164      * Fit-to-page ticket item.
165      * @type {!print_preview.ticket_items.FitToPage}
166      * @private
167      */
168     this.fitToPage_ = new print_preview.ticket_items.FitToPage(
169         this.documentInfo_, this.destinationStore_);
171     /**
172      * Print CSS backgrounds ticket item.
173      * @type {!print_preview.ticket_items.CssBackground}
174      * @private
175      */
176     this.cssBackground_ = new print_preview.ticket_items.CssBackground(
177         this.appState_, this.documentInfo_);
179     /**
180      * Print selection only ticket item.
181      * @type {!print_preview.ticket_items.SelectionOnly}
182      * @private
183      */
184     this.selectionOnly_ =
185         new print_preview.ticket_items.SelectionOnly(this.documentInfo_);
187     /**
188      * Print friendly ticket item.
189      * @type {!print_preview.ticket_items.DistillPage}
190      * @private
191      */
192     this.distillPage_ = new print_preview.ticket_items.DistillPage(
193         this.documentInfo_);
195     /**
196      * Vendor ticket items.
197      * @type {!print_preview.ticket_items.VendorItems}
198      * @private
199      */
200     this.vendorItems_ = new print_preview.ticket_items.VendorItems(
201         this.appState_, this.destinationStore_);
203     /**
204      * Keeps track of event listeners for the print ticket store.
205      * @type {!EventTracker}
206      * @private
207      */
208     this.tracker_ = new EventTracker();
210     /**
211      * Whether the print preview has been initialized.
212      * @type {boolean}
213      * @private
214      */
215     this.isInitialized_ = false;
217     this.addEventListeners_();
218   };
220   /**
221    * Event types dispatched by the print ticket store.
222    * @enum {string}
223    */
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'
229   };
231   PrintTicketStore.prototype = {
232     __proto__: cr.EventTarget.prototype,
234     /**
235      * Whether the print preview has been initialized.
236      * @type {boolean}
237      */
238     get isInitialized() {
239       return this.isInitialized_;
240     },
242     get collate() {
243       return this.collate_;
244     },
246     get color() {
247       return this.color_;
248     },
250     get copies() {
251       return this.copies_;
252     },
254     get cssBackground() {
255       return this.cssBackground_;
256     },
258     get customMargins() {
259       return this.customMargins_;
260     },
262     get dpi() {
263       return this.dpi_;
264     },
266     get duplex() {
267       return this.duplex_;
268     },
270     get fitToPage() {
271       return this.fitToPage_;
272     },
274     get headerFooter() {
275       return this.headerFooter_;
276     },
278     get distillPage() {
279       return this.distillPage_;
280     },
282     get mediaSize() {
283       return this.mediaSize_;
284     },
286     get landscape() {
287       return this.landscape_;
288     },
290     get marginsType() {
291       return this.marginsType_;
292     },
294     get pageRange() {
295       return this.pageRange_;
296     },
298     get selectionOnly() {
299       return this.selectionOnly_;
300     },
302     get vendorItems() {
303       return this.vendorItems_;
304     },
306     /**
307      * @return {!print_preview.MeasurementSystem} Measurement system of the
308      *     local system.
309      */
310     get measurementSystem() {
311       return this.measurementSystem_;
312     },
314     /**
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
321      *     printed.
322      */
323     init: function(
324         thousandsDelimeter, decimalDelimeter, unitType, selectionOnly) {
325       this.measurementSystem_.setSystem(thousandsDelimeter, decimalDelimeter,
326                                         unitType);
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)));
335       }
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)));
340       }
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)));
346       }
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)));
351       }
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)));
357       }
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)));
363       }
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)));
369       }
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)));
375       }
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)));
381       }
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)));
387       }
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)));
393     }
394     },
396     /**
397      * @return {boolean} {@code true} if the stored print ticket is valid,
398      *     {@code false} otherwise.
399      */
400     isTicketValid: function() {
401       return this.isTicketValidForPreview() &&
402           (!this.copies_.isCapabilityAvailable() || this.copies_.isValid()) &&
403           (!this.pageRange_.isCapabilityAvailable() ||
404               this.pageRange_.isValid());
405     },
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());
413     },
415     /**
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.
419      */
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');
429       var cjt = {
430         version: '1.0',
431         print: {}
432       };
433       if (this.collate.isCapabilityAvailable() && this.collate.isUserEdited()) {
434         cjt.print.collate = {collate: this.collate.getValue()};
435       }
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');
440         } else {
441           cjt.print.color = {type: selectedOption.type};
442           if (selectedOption.hasOwnProperty('vendor_id')) {
443             cjt.print.color.vendor_id = selectedOption.vendor_id;
444           }
445         }
446       }
447       if (this.copies.isCapabilityAvailable() && this.copies.isUserEdited()) {
448         cjt.print.copies = {copies: this.copies.getValueAsNumber()};
449       }
450       if (this.duplex.isCapabilityAvailable() && this.duplex.isUserEdited()) {
451         cjt.print.duplex =
452             {type: this.duplex.getValue() ? 'LONG_EDGE' : 'NO_DUPLEX'};
453       }
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
461         };
462       }
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'};
472       }
473       if (this.dpi.isCapabilityAvailable()) {
474         var value = this.dpi.getValue();
475         cjt.print.dpi = {
476           horizontal_dpi: value.horizontal_dpi,
477           vertical_dpi: value.vertical_dpi,
478           vendor_id: value.vendor_id
479         };
480       }
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]});
489           }
490         }
491       }
492       return JSON.stringify(cjt);
493     },
495     /**
496      * Adds event listeners for the print ticket store.
497      * @private
498      */
499     addEventListeners_: function() {
500       this.tracker_.add(
501           this.destinationStore_,
502           print_preview.DestinationStore.EventType.DESTINATION_SELECT,
503           this.onDestinationSelect_.bind(this));
505       this.tracker_.add(
506           this.destinationStore_,
507           print_preview.DestinationStore.EventType.
508               SELECTED_DESTINATION_CAPABILITIES_READY,
509           this.onSelectedDestinationCapabilitiesReady_.bind(this));
511       this.tracker_.add(
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.
521       this.tracker_.add(
522           this.documentInfo_,
523           print_preview.DocumentInfo.EventType.CHANGE,
524           this.onDocumentInfoChange_.bind(this));
525     },
527     /**
528      * Called when the destination selected.
529      * @private
530      */
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);
539         }
540         this.vendorItems_.updateValue({});
541       }
542     },
544     /**
545      * Called when the capabilities of the selected destination are ready.
546      * @private
547      */
548     onSelectedDestinationCapabilitiesReady_: function() {
549       var caps = assert(
550           this.destinationStore_.selectedDestination.capabilities);
551       var isFirstUpdate = this.capabilitiesHolder_.get() == null;
552       this.capabilitiesHolder_.set(caps);
553       if (isFirstUpdate) {
554         this.isInitialized_ = true;
555         cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE);
556       } else {
557         cr.dispatchSimpleEvent(
558             this, PrintTicketStore.EventType.CAPABILITIES_CHANGE);
559       }
560     },
562     /**
563      * Called when document data model has changed. Dispatches a print ticket
564      * store event.
565      * @private
566      */
567     onDocumentInfoChange_: function() {
568       cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
569     },
570   };
572   // Export
573   return {
574     PrintTicketStore: PrintTicketStore
575   };