• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5cr.define('print_preview', function() {
6  'use strict';
7
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.
12
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);
27
28    /**
29     * Destination store used to understand which printer is selected.
30     * @type {!print_preview.DestinationStore}
31     * @private
32     */
33    this.destinationStore_ = destinationStore;
34
35    /**
36     * App state used to persist and load ticket values.
37     * @type {!print_preview.AppState}
38     * @private
39     */
40    this.appState_ = appState;
41
42    /**
43     * Information about the document to print.
44     * @type {!print_preview.DocumentInfo}
45     * @private
46     */
47    this.documentInfo_ = documentInfo;
48
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();
55
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);
63
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_);
71
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_);
79
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_);
87
88    /**
89     * Duplex ticket item.
90     * @type {!print_preview.ticket_items.Duplex}
91     * @private
92     */
93    this.duplex_ = new print_preview.ticket_items.Duplex(
94        this.appState_, this.destinationStore_);
95
96    /**
97     * Page range ticket item.
98     * @type {!print_preview.ticket_items.PageRange}
99     * @private
100     */
101    this.pageRange_ =
102        new print_preview.ticket_items.PageRange(this.documentInfo_);
103
104    /**
105     * Custom margins ticket item.
106     * @type {!print_preview.ticket_items.CustomMargins}
107     * @private
108     */
109    this.customMargins_ = new print_preview.ticket_items.CustomMargins(
110        this.appState_, this.documentInfo_);
111
112    /**
113     * Margins type ticket item.
114     * @type {!print_preview.ticket_items.MarginsType}
115     * @private
116     */
117    this.marginsType_ = new print_preview.ticket_items.MarginsType(
118        this.appState_, this.documentInfo_, this.customMargins_);
119
120    /**
121     * Landscape ticket item.
122     * @type {!print_preview.ticket_items.Landscape}
123     * @private
124     */
125    this.landscape_ = new print_preview.ticket_items.Landscape(
126        this.appState_, this.destinationStore_, this.documentInfo_,
127        this.marginsType_, this.customMargins_);
128
129    /**
130     * Header-footer ticket item.
131     * @type {!print_preview.ticket_items.HeaderFooter}
132     * @private
133     */
134    this.headerFooter_ = new print_preview.ticket_items.HeaderFooter(
135        this.appState_, this.documentInfo_, this.marginsType_,
136        this.customMargins_);
137
138    /**
139     * Fit-to-page ticket item.
140     * @type {!print_preview.ticket_items.FitToPage}
141     * @private
142     */
143    this.fitToPage_ = new print_preview.ticket_items.FitToPage(
144        this.documentInfo_, this.destinationStore_);
145
146    /**
147     * Print CSS backgrounds ticket item.
148     * @type {!print_preview.ticket_items.CssBackground}
149     * @private
150     */
151    this.cssBackground_ = new print_preview.ticket_items.CssBackground(
152        this.appState_, this.documentInfo_);
153
154    /**
155     * Print selection only ticket item.
156     * @type {!print_preview.ticket_items.SelectionOnly}
157     * @private
158     */
159    this.selectionOnly_ =
160        new print_preview.ticket_items.SelectionOnly(this.documentInfo_);
161
162    /**
163     * Keeps track of event listeners for the print ticket store.
164     * @type {!EventTracker}
165     * @private
166     */
167    this.tracker_ = new EventTracker();
168
169    /**
170     * Whether the print preview has been initialized.
171     * @type {boolean}
172     * @private
173     */
174    this.isInitialized_ = false;
175
176    this.addEventListeners_();
177  };
178
179  /**
180   * Event types dispatched by the print ticket store.
181   * @enum {string}
182   */
183  PrintTicketStore.EventType = {
184    CAPABILITIES_CHANGE: 'print_preview.PrintTicketStore.CAPABILITIES_CHANGE',
185    DOCUMENT_CHANGE: 'print_preview.PrintTicketStore.DOCUMENT_CHANGE',
186    INITIALIZE: 'print_preview.PrintTicketStore.INITIALIZE',
187    TICKET_CHANGE: 'print_preview.PrintTicketStore.TICKET_CHANGE'
188  };
189
190  PrintTicketStore.prototype = {
191    __proto__: cr.EventTarget.prototype,
192
193    /**
194     * Whether the print preview has been initialized.
195     * @type {boolean}
196     */
197    get isInitialized() {
198      return this.isInitialized_;
199    },
200
201    get collate() {
202      return this.collate_;
203    },
204
205    get color() {
206      return this.color_;
207    },
208
209    get copies() {
210      return this.copies_;
211    },
212
213    get cssBackground() {
214      return this.cssBackground_;
215    },
216
217    get customMargins() {
218      return this.customMargins_;
219    },
220
221    get duplex() {
222      return this.duplex_;
223    },
224
225    get fitToPage() {
226      return this.fitToPage_;
227    },
228
229    get headerFooter() {
230      return this.headerFooter_;
231    },
232
233    get landscape() {
234      return this.landscape_;
235    },
236
237    get marginsType() {
238      return this.marginsType_;
239    },
240
241    get pageRange() {
242      return this.pageRange_;
243    },
244
245    get selectionOnly() {
246      return this.selectionOnly_;
247    },
248
249    /**
250     * @return {!print_preview.MeasurementSystem} Measurement system of the
251     *     local system.
252     */
253    get measurementSystem() {
254      return this.measurementSystem_;
255    },
256
257    /**
258     * Initializes the print ticket store. Dispatches an INITIALIZE event.
259     * @param {string} thousandsDelimeter Delimeter of the thousands place.
260     * @param {string} decimalDelimeter Delimeter of the decimal point.
261     * @param {!print_preview.MeasurementSystem.UnitType} unitType Type of unit
262     *     of the local measurement system.
263     * @param {boolean} selectionOnly Whether only selected content should be
264     *     printed.
265     */
266    init: function(
267        thousandsDelimeter, decimalDelimeter, unitType, selectionOnly) {
268      this.measurementSystem_.setSystem(thousandsDelimeter, decimalDelimeter,
269                                        unitType);
270      this.selectionOnly_.updateValue(selectionOnly);
271
272      // Initialize ticket with user's previous values.
273      if (this.appState_.hasField(
274          print_preview.AppState.Field.IS_COLOR_ENABLED)) {
275        this.color_.updateValue(this.appState_.getField(
276            print_preview.AppState.Field.IS_COLOR_ENABLED));
277      }
278      if (this.appState_.hasField(
279          print_preview.AppState.Field.IS_DUPLEX_ENABLED)) {
280        this.duplex_.updateValue(this.appState_.getField(
281            print_preview.AppState.Field.IS_DUPLEX_ENABLED));
282      }
283      if (this.appState_.hasField(
284          print_preview.AppState.Field.IS_LANDSCAPE_ENABLED)) {
285        this.landscape_.updateValue(this.appState_.getField(
286            print_preview.AppState.Field.IS_LANDSCAPE_ENABLED));
287      }
288      // Initialize margins after landscape because landscape may reset margins.
289      if (this.appState_.hasField(print_preview.AppState.Field.MARGINS_TYPE)) {
290        this.marginsType_.updateValue(
291            this.appState_.getField(print_preview.AppState.Field.MARGINS_TYPE));
292      }
293      if (this.appState_.hasField(
294          print_preview.AppState.Field.CUSTOM_MARGINS)) {
295        this.customMargins_.updateValue(this.appState_.getField(
296            print_preview.AppState.Field.CUSTOM_MARGINS));
297      }
298      if (this.appState_.hasField(
299          print_preview.AppState.Field.IS_HEADER_FOOTER_ENABLED)) {
300        this.headerFooter_.updateValue(this.appState_.getField(
301            print_preview.AppState.Field.IS_HEADER_FOOTER_ENABLED));
302      }
303      if (this.appState_.hasField(
304          print_preview.AppState.Field.IS_COLLATE_ENABLED)) {
305        this.collate_.updateValue(this.appState_.getField(
306            print_preview.AppState.Field.IS_COLLATE_ENABLED));
307      }
308      if (this.appState_.hasField(
309          print_preview.AppState.Field.IS_CSS_BACKGROUND_ENABLED)) {
310        this.cssBackground_.updateValue(this.appState_.getField(
311            print_preview.AppState.Field.IS_CSS_BACKGROUND_ENABLED));
312      }
313    },
314
315    /**
316     * @return {boolean} {@code true} if the stored print ticket is valid,
317     *     {@code false} otherwise.
318     */
319    isTicketValid: function() {
320      return this.isTicketValidForPreview() &&
321          (!this.pageRange_.isCapabilityAvailable() ||
322              this.pageRange_.isValid());
323    },
324
325    /** @return {boolean} Whether the ticket is valid for preview generation. */
326    isTicketValidForPreview: function() {
327      return (!this.copies.isCapabilityAvailable() || this.copies.isValid()) &&
328          (!this.marginsType_.isCapabilityAvailable() ||
329              !this.marginsType_.isValueEqual(
330                  print_preview.ticket_items.MarginsType.Value.CUSTOM) ||
331              this.customMargins_.isValid());
332    },
333
334    /**
335     * Creates an object that represents a Google Cloud Print print ticket.
336     * @param {!print_preview.Destination} destination Destination to print to.
337     * @return {!Object} Google Cloud Print print ticket.
338     */
339    createPrintTicket: function(destination) {
340      assert(!destination.isLocal || destination.isPrivet,
341             'Trying to create a Google Cloud Print print ticket for a local ' +
342                 ' non-privet destination');
343
344      assert(destination.capabilities,
345             'Trying to create a Google Cloud Print print ticket for a ' +
346                 'destination with no print capabilities');
347      var cjt = {
348        version: '1.0',
349        print: {}
350      };
351      if (this.collate.isCapabilityAvailable() && this.collate.isUserEdited()) {
352        cjt.print.collate = {collate: this.collate.getValue() == 'true'};
353      }
354      if (this.color.isCapabilityAvailable() && this.color.isUserEdited()) {
355        var colorType = this.color.getValue() ?
356            'STANDARD_COLOR' : 'STANDARD_MONOCHROME';
357        // Find option with this colorType to read its vendor_id.
358        var selectedOptions = destination.capabilities.printer.color.option.
359            filter(function(option) {
360              return option.type == colorType;
361            });
362        if (selectedOptions.length == 0) {
363          console.error('Could not find correct color option');
364        } else {
365          cjt.print.color = {type: colorType};
366          if (selectedOptions[0].hasOwnProperty('vendor_id')) {
367            cjt.print.color.vendor_id = selectedOptions[0].vendor_id;
368          }
369        }
370      }
371      if (this.copies.isCapabilityAvailable() && this.copies.isUserEdited()) {
372        cjt.print.copies = {copies: this.copies.getValueAsNumber()};
373      }
374      if (this.duplex.isCapabilityAvailable() && this.duplex.isUserEdited()) {
375        cjt.print.duplex =
376            {type: this.duplex.getValue() ? 'LONG_EDGE' : 'NO_DUPLEX'};
377      }
378      if (this.landscape.isCapabilityAvailable() &&
379          this.landscape.isUserEdited()) {
380        cjt.print.page_orientation =
381            {type: this.landscape.getValue() ? 'LANDSCAPE' : 'PORTRAIT'};
382      }
383      return JSON.stringify(cjt);
384    },
385
386    /**
387     * Adds event listeners for the print ticket store.
388     * @private
389     */
390    addEventListeners_: function() {
391      this.tracker_.add(
392          this.destinationStore_,
393          print_preview.DestinationStore.EventType.DESTINATION_SELECT,
394          this.onDestinationSelect_.bind(this));
395
396      this.tracker_.add(
397          this.destinationStore_,
398          print_preview.DestinationStore.EventType.
399              SELECTED_DESTINATION_CAPABILITIES_READY,
400          this.onSelectedDestinationCapabilitiesReady_.bind(this));
401
402      this.tracker_.add(
403          this.destinationStore_,
404          print_preview.DestinationStore.EventType.
405              CACHED_SELECTED_DESTINATION_INFO_READY,
406          this.onSelectedDestinationCapabilitiesReady_.bind(this));
407
408      // TODO(rltoscano): Print ticket store shouldn't be re-dispatching these
409      // events, the consumers of the print ticket store events should listen
410      // for the events from document info instead. Will move this when
411      // consumers are all migrated.
412      this.tracker_.add(
413          this.documentInfo_,
414          print_preview.DocumentInfo.EventType.CHANGE,
415          this.onDocumentInfoChange_.bind(this));
416    },
417
418    /**
419     * Called when the destination selected.
420     * @private
421     */
422    onDestinationSelect_: function() {
423      // Reset user selection for certain ticket items.
424      if (this.capabilitiesHolder_.get() != null) {
425        this.customMargins_.updateValue(null);
426        if (this.marginsType_.getValue() ==
427            print_preview.ticket_items.MarginsType.Value.CUSTOM) {
428          this.marginsType_.updateValue(
429              print_preview.ticket_items.MarginsType.Value.DEFAULT);
430        }
431      }
432    },
433
434    /**
435     * Called when the capabilities of the selected destination are ready.
436     * @private
437     */
438    onSelectedDestinationCapabilitiesReady_: function() {
439      var caps = this.destinationStore_.selectedDestination.capabilities;
440      var isFirstUpdate = this.capabilitiesHolder_.get() == null;
441      this.capabilitiesHolder_.set(caps);
442      if (isFirstUpdate) {
443        this.isInitialized_ = true;
444        cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.INITIALIZE);
445      } else {
446        cr.dispatchSimpleEvent(
447            this, PrintTicketStore.EventType.CAPABILITIES_CHANGE);
448      }
449    },
450
451    /**
452     * Called when document data model has changed. Dispatches a print ticket
453     * store event.
454     * @private
455     */
456    onDocumentInfoChange_: function() {
457      cr.dispatchSimpleEvent(this, PrintTicketStore.EventType.DOCUMENT_CHANGE);
458    },
459  };
460
461  // Export
462  return {
463    PrintTicketStore: PrintTicketStore
464  };
465});
466