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